diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..844d5e34ca86241eeb9d1866a7d08be55ac5a743 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,14 @@ + +Original author +--------------- + +Lee David Painter + +Contributors +------------ + +Brett Smith +Richard Pernavas +Erwin Bolwidt +Sascha Hunold <hunoldinho@users.sourceforge.net> + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000000000000000000000000000000000000..e3c66fbc074922bd57843980b2e437bb84666762 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,33 @@ + +Version 0.2.9 +------------- + +* com/sshtools/j2ssh/io/ByteArrayWriter.java (sahu) + - UFT-8 patch + +* applied patch number [977590] + - possible deadlock in transport lock handling + +* made source compilable with Java 5 (sahu) + - enum -> en + - changed getState() -> getStartStopState() [SessionChannelServer.java] + +* cosmetic refactoring (sahu) + - removed calls to deprecated methods + - example: hide() replaced by setVisible() + +* com/sshtools/j2ssh/sftp/FileAttributes.java (sahu) + - fixed test for symbolic links + +* com/sshtools/j2ssh/sftp/SftpFile.java (sahu) + - changed permission handling + +* rekey patch (sahu) + - rekey bug when more than 1 GB is transferred + + +Version 0.2.8 +------------- + +- changed license to GPL only +- changed ant build.xml file to compile j2ssh with Java > 1.4 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000000000000000000000000000000000000..1240b6950befd0e82801c3c31adde95ba948ef07 --- /dev/null +++ b/INSTALL @@ -0,0 +1,53 @@ + +==================== +Using Sshtools J2SSH +==================== + +1) First unzip the distribution file into the PATH_OF_YOUR_CHOICE which we refer to as $INSTALL_DIR. +2) Build the JAR files using the ant script $INSTALL_DIR/build.xml +3) Add the JAR files to your class path. + +========= +JAR files +========= +The Sshtools distribution comes a number jar files, they are + +j2ssh-core-VERSION.jar +The core jar file contains SSH client components, the J2SSH core now implements +most of the basic ssh specifications and implements a number of clients including ssh, sftp and scp. +The core also includes the ssh agent implementation, port forwarding, proxy components and a framework +for extending channels, subsystems and all ssh protocol algorithms. + +j2ssh-ant-VERSION.jar +This file contains the J2SSH ant tasks + +j2ssh-common-VERSION.jar +The common jar file contains a number of reusable components such as Swing authentication +prompts, an xml configuration context, remote identification and configuration automation utilites. + +j2ssh-daemon-VERSION.jar +The daemon file implements the server side components in conjunction with the j2ssh core. + +===================== +J2SSH dependencies +===================== + +The dependencies vary for J2SSH depending upon the JDK and individual J2SSH jar files required +by your implementation. + +If you are using JDK 1.4 or greater, the only dependency the j2ssh jar files require +is commons-logging.jar with the exception of the J2SSH ant tasks which also require ant.jar + +For JDK 1.3.1 the core requires a JCE provider. We have tested the core using the bouncycastle JCE which +is provided with the JDK 1.3.1 distribution file. If you are using the common or daemon jar files with +JDK 1.3.1 you will also need to include xerces for xml parsing which is also included. + +================= +Building Sshtools +================= + +If you downloaded the source distribution you need to build the binaries before +using sshtools. Like most java appilications today, Sshtools relies on ANT as +its buildtool. ANT is availale from "http://jakarta.apache.org/ant/". ANT +requires a build file called build.xml which can be found in the $INSTALL_DIR +directory. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d511905c1647a1e311e8b20d5930a37a9c2531cd --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/build.xml b/build.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c867eec4bfb3e1597d2f331fb3c0bc455c47ee0 --- /dev/null +++ b/build.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="sshtools" default="build" basedir="."> + <!-- Set global properties for this build --> + <property name="build.examples" value="./examples"/> + <property name="build.dist" value="./dist"/> + <property name="build.conf" value="./conf"/> + <property name="build.dist.classes" value="${build.dist}/classes"/> + <property name="build.dist.lib" value="${build.dist}/lib"/> + <property name="build.dependency" value="./lib"/> + <property name="build.src" value="./src"/> + <property name="build.docs" value="./docs"/> + + <!-- Global build parameters --> + <property file="j2ssh.properties"/> + + <!-- Set this to 'yes' if you wish the classes + to be compiled with debug information --> + <property name="build.debugInformation" value="off"/> + + <!-- Build the project classpath --> + <path id="project.class.path"> + <fileset dir="${build.dependency}"> + <include name="*.jar"/> + </fileset> + <pathelement path="${build.dist.classes}/"/> + </path> + + <target name="compile" depends="clean"> + <echo message="Creating directories"/> + <!-- Create the output directory --> + <mkdir dir="${build.dist}"/> + <mkdir dir="${build.dist.classes}"/> + <!-- Copy the projects resources to the classpath --> + <echo message="Copying resource to classpath"/> + <copy todir="${build.dist.classes}" > + <fileset dir="${build.src}" > + <include name="**/*.png"/> + <include name="**/*.gif"/> + <include name="**/*.xpm"/> + <include name="**/*.ico"/> + </fileset> + </copy> + <!-- Compile the source --> + <javac srcdir="${build.src}" debug="${build.debugInformation}" + destdir="${build.dist.classes}" includes="**/*.java" + source="1.4" target="1.4"> + <classpath refid="project.class.path"/> + </javac> + + </target> + <target name="build" depends="compile"> + <mkdir dir="${build.dist.lib}"/> + + <!-- Build the J2SSH library files --> + <jar jarfile="${build.dist.lib}/j2ssh-core-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}.jar" basedir="${build.dist.classes}"> + <include name="com/sshtools/j2ssh/**/*.class"/> + <manifest> + <attribute name="Product-Version" + value="${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}"/> + </manifest> + </jar> + + <jar jarfile="${build.dist.lib}/j2ssh-ant-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}.jar" basedir="${build.dist.classes}"> + <include name="com/sshtools/ant/**/*.class"/> + <manifest> + <attribute name="Product-Version" + value="${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}"/> + </manifest> + </jar> + + <jar jarfile="${build.dist.lib}/j2ssh-dameon-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}.jar" basedir="${build.dist.classes}"> + <include name="com/sshtools/daemon/**/*.class"/> + <exclude name="com/sshtools/daemon/windows/**/*.*"/> + <exclude name="com/sshtools/daemon/linux/**/*.*"/> + <manifest> + <attribute name="Product-Version" + value="${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}"/> + </manifest> + </jar> + + <jar jarfile="${build.dist.lib}/j2ssh-common-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}.jar" basedir="${build.dist.classes}"> + <include name="com/sshtools/common/**/*.class"/> + <include name="com/sshtools/common/**/*.png"/> + <include name="com/sshtools/common/**/*.gif"/> + <manifest> + <attribute name="Product-Version" + value="${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}"/> + </manifest> + </jar> + </target> + + <!-- JAVADOCS TARGET --> + <target name="javadoc" depends="build"> + + <mkdir dir="${build.docs}"/> + + <javadoc packagenames="com.sshtools.*" + sourcepath="${build.src}" + defaultexcludes="yes" + destdir="${build.docs}" + author="true" + version="true" + use="true" + windowtitle="J2SSH Javadocs" + notree="true"> + <doctitle><![CDATA[<h1>SSHTools J2SSH</h1><br> + <p>SSH (Secure Shell) is a program to log into another computer over a network, to execute commands in a +remote machine and to move files from one machine to another. It provides strong authentication and secure +communication over insecure networks.</p>]]></doctitle> +<bottom><![CDATA[<i>Copyright © 2002-2003 Lee David Painter & Contributors. All Rights Reserved.</i>]]></bottom> +</javadoc> + + </target> + + <!-- Clean up all the generated files --> + <target name="clean" > + <!-- delete the classpath --> + <delete dir="${build.dist.classes}"/> + <delete dir="${build.dist.lib}"/> + <delete dir="${build.dist}"/> + </target> + + + <target name="release"> + + <delete dir="./release"/> + <mkdir dir="./release"/> + <zip zipfile="./release/j2ssh-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}-src.zip"> + <zipfileset dir="${build.src}" includes="**/*.java" prefix="j2ssh/src"/> + <zipfileset dir="${build.src}" includes="**/*.png" prefix="j2ssh/src"/> + <zipfileset dir="${build.src}" includes="**/*.gif" prefix="j2ssh/src"/> + <zipfileset dir="${build.src}" includes="**/*.ico" prefix="j2ssh/src"/> + <zipfileset dir="${build.src}" includes="**/*.xpm" prefix="j2ssh/src"/> + <zipfileset dir="${build.conf}" includes="*.xml" prefix="j2ssh/conf"/> + <zipfileset dir="${build.dependency}" includes="*.jar" prefix="j2ssh/lib"/> + <zipfileset dir="${build.dependency}" includes="*.LICENSE" prefix="j2ssh/lib"/> + <zipfileset dir="${build.examples}" includes="*.java" prefix="j2ssh/examples"/> + <zipfileset dir="." includes="build.xml,j2ssh.properties,INSTALL,LICENSE,ChangeLog,AUTHORS" prefix="j2ssh"/> + </zip> + + <unzip src="./release/j2ssh-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}-src.zip" + dest="./release/j2ssh"/> + <tar tarfile="./release/j2ssh-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}-src.tar" + basedir="./release/j2ssh"/> + <gzip zipfile="./release/j2ssh-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}-src.tar.gz" + src="./release/j2ssh-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}-src.tar"/> + + <delete file="./release/j2ssh-${j2ssh.version.major}.${j2ssh.version.minor}.${j2ssh.version.build}-src.tar"/> + <delete dir="./release/j2ssh"/> + </target> +</project> diff --git a/conf/authorization.xml b/conf/authorization.xml new file mode 100644 index 0000000000000000000000000000000000000000..b78294933a83ee6dfada0542c0f1958ab597e96a --- /dev/null +++ b/conf/authorization.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- Sshtools User Authorization File --> +<AuthorizedKeys> + <!-- Enter authorized public key elements here --> + <!--<Key>dsa.pub</Key> --> + +</AuthorizedKeys> diff --git a/conf/automation.xml b/conf/automation.xml new file mode 100644 index 0000000000000000000000000000000000000000..fddba1b588daca2d772c3a6257be1bc01a12bc78 --- /dev/null +++ b/conf/automation.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- SSHTools automation file, defines mappings for automated configuration --> +<Automation> + <RemoteIdentification defaultName="OpenSSH"> + <Rule startsWith="OpenSSH"/> + <Rule startsWith="OpenSSH" contains="3.4" name="OpenSSH 3.4" priority="1"/> + <AuthorizedKeysFormat implementationClass="com.sshtools.common.automate.OpenSSHAuthorizedKeysFormat" + defaultPath=".ssh/authorized_keys"/> + </RemoteIdentification> + <RemoteIdentification defaultName="SSHTools"> + <Rule startsWith="http://www.sshtools.com"/> + <AuthorizedKeysFormat implementationClass="com.sshtools.common.automate.SshtoolsAuthorizedKeysFormat" + defaultPath=".ssh2/authorization.xml"/> + </RemoteIdentification> + <RemoteIdentification defaultName="SSH2"> + <Rule contains="SSH Secure Shell"/> + <AuthorizedKeysFormat implementationClass="com.sshtools.common.automate.SSH2AuthorizedKeysFormat" + defaultPath=".ssh2/authorization"/> + </RemoteIdentification> + +</Automation> \ No newline at end of file diff --git a/conf/platform.xml b/conf/platform.xml new file mode 100644 index 0000000000000000000000000000000000000000..aa4516b574e38fd43ead91d321743cc46bf2e49f --- /dev/null +++ b/conf/platform.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Platform configuration file - Determines the behaviour of platform specific services +--> +<PlatformConfiguration> + <!-- The process provider for executing and redirecting a process --> + <NativeProcessProvider></NativeProcessProvider> + <!-- The authentication provider for authenticating users and obtaining user information --> + <NativeAuthenticationProvider></NativeAuthenticationProvider> + <!-- The file system provider for SFTP --> + <NativeFileSystemProvider></NativeFileSystemProvider> + <!-- Native settings which may be used by the process or authentication provider --> + <!-- Add native settings here --> + <!-- <NativeSetting Name="AuthenticateOnDomain" Value="."/> --> +</PlatformConfiguration> diff --git a/conf/sshtools.xml b/conf/sshtools.xml new file mode 100644 index 0000000000000000000000000000000000000000..30e49245a9b41085b20d1a0f78e29f6e68540c5e --- /dev/null +++ b/conf/sshtools.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- The Java SSH API Configuration file --> +<SshAPIConfiguration> + + <!-- The Cipher configuration, add or overide default cipher implementations --> + <CipherConfiguration> + <DefaultAlgorithm>blowfish-cbc</DefaultAlgorithm> + </CipherConfiguration> + <!-- The Message Authentication Code configuration, add or overide default mac implementations --> + <MacConfiguration> + <DefaultAlgorithm>hmac-md5</DefaultAlgorithm> + </MacConfiguration> + <!-- The Compression configuration, add or overide default compression implementations --> + <CompressionConfiguration> + <DefaultAlgorithm>none</DefaultAlgorithm> + </CompressionConfiguration> + <!-- The Public Key configuration, add or overide default public key implementations --> + <PublicKeyConfiguration> + <DefaultAlgorithm>ssh-dss</DefaultAlgorithm> + + <DefaultPublicFormat>SECSH-PublicKey-Base64Encoded</DefaultPublicFormat> + <DefaultPrivateFormat>SSHTools-PrivateKey-Base64Encoded</DefaultPrivateFormat> + </PublicKeyConfiguration> + <!-- The Authentication configuration, add or overide default authentication implementations --> + <AuthenticationConfiguration> + + </AuthenticationConfiguration> + <!-- The Key Exchange configuration, add or overide default Key Exchange implementations --> + <KeyExchangeConfiguration> + <DefaultAlgorithm>diffie-hellman-group1-sha1</DefaultAlgorithm> + </KeyExchangeConfiguration> +</SshAPIConfiguration> diff --git a/examples/KBIConnect.java b/examples/KBIConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..6cfd3c59dcb6e2caa4aa8eef44931cca3edc4d66 --- /dev/null +++ b/examples/KBIConnect.java @@ -0,0 +1,133 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.KBIAuthenticationClient; +import com.sshtools.j2ssh.authentication.KBIPrompt; +import com.sshtools.j2ssh.authentication.KBIRequestHandler; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.SshConnectionProperties; +import com.sshtools.j2ssh.io.IOStreamConnector; +import com.sshtools.j2ssh.io.IOStreamConnectorState; +import com.sshtools.j2ssh.session.SessionChannelClient; +/*import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter;*/ +/** + * Demonstrates a simple password connection to an SSH server. + * + * @author <A HREF="mailto:lee@sshtools.com">Lee David Painter</A> + * @version $Id: KBIConnect.java,v 1.8 2003/07/16 10:42:07 t_magicthize Exp $ + * + * @created 20 December 2002 + */ +public class KBIConnect { + private static BufferedReader reader = + new BufferedReader(new InputStreamReader(System.in)); + /** + * The main program for the PasswordConnect class + * + * @param args The command line arguments + */ + public static void main(String args[]) { + try { + // Setup a logfile + /*Handler fh = new FileHandler("example.log"); + fh.setFormatter(new SimpleFormatter()); + Logger.getLogger("com.sshtools").setUseParentHandlers(false); + Logger.getLogger("com.sshtools").addHandler(fh); + Logger.getLogger("com.sshtools").setLevel(Level.ALL);*/ + // Configure J2SSH (This will attempt to install the bouncycastle provider + // under jdk 1.3.1) + ConfigurationLoader.initialize(false); + System.out.print("Connect to host? "); + System.out.print("Connect to host? "); + String hostname = reader.readLine(); + // Make a client connection + SshClient ssh = new SshClient(); + SshConnectionProperties properties = new SshConnectionProperties(); + properties.setHost(hostname); + // Connect to the host + ssh.connect(properties); + // Create a password authentication instance + KBIAuthenticationClient kbi = new KBIAuthenticationClient(); + // Get the users name + System.out.print("Username? "); + // Read the password + String username = reader.readLine(); + kbi.setUsername(username); + kbi.setKBIRequestHandler(new KBIRequestHandler() { + public void showPrompts(String name, String instructions, + KBIPrompt[] prompts) { + System.out.println(name); + System.out.println(instructions); + String response; + if (prompts != null) { + for (int i = 0; i < prompts.length; i++) { + System.out.print(prompts[i].getPrompt() + ": "); + try { + response = reader.readLine(); + prompts[i].setResponse(response); + } + catch (IOException ex) { + prompts[i].setResponse(""); + ex.printStackTrace(); + } + } + } + } + }); + // Try the authentication + int result = ssh.authenticate(kbi); + // Evaluate the result + if (result == AuthenticationProtocolState.COMPLETE) { + // The connection is authenticated we can now do some real work! + SessionChannelClient session = ssh.openSessionChannel(); + if(!session.requestPseudoTerminal("vt100", 80, 24, 0, 0, "")) + System.out.println("Failed to allocate a pseudo terminal"); + if(session.startShell()) { + IOStreamConnector input = + new IOStreamConnector(System.in, session.getOutputStream()); + IOStreamConnector output = + new IOStreamConnector(session.getInputStream(), System.out); + output.getState().waitForState(IOStreamConnectorState.CLOSED); + }else + System.out.println("Failed to start the users shell"); + ssh.disconnect(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/examples/PasswordConnect.java b/examples/PasswordConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..fbbdcaf1b0ca0ae5dc98f44b42d8de5745bb8b73 --- /dev/null +++ b/examples/PasswordConnect.java @@ -0,0 +1,122 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import java.io.BufferedReader; +import java.io.InputStreamReader; +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient; +import com.sshtools.j2ssh.configuration.SshConnectionProperties; +import com.sshtools.j2ssh.connection.ChannelState; +import com.sshtools.j2ssh.io.IOStreamConnector; +import com.sshtools.j2ssh.session.SessionChannelClient; +import com.sshtools.j2ssh.transport.TransportProtocolEventHandler; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +// JDK > 1.4 ONLY +/*import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter;*/ +/** + * Demonstrates a simple password connection to an SSH server. + * + * @author <A HREF="mailto:lee@sshtools.com">Lee David Painter</A> + * @version $Id: PasswordConnect.java,v 1.12 2003/07/16 10:42:07 t_magicthize Exp $ + * + * @created 20 December 2002 + */ +public class PasswordConnect { + /** + * The main program for the PasswordConnect class + * + * @param args The command line arguments + */ + public static void main(String args[]) { + try { + // JDK > 1.4 ONLY + /*Handler fh = new FileHandler("example.log"); + fh.setFormatter(new SimpleFormatter()); + Logger.getLogger("com.sshtools").setUseParentHandlers(false); + Logger.getLogger("com.sshtools").addHandler(fh); + Logger.getLogger("com.sshtools").setLevel(Level.ALL);*/ + // Configure J2SSH (This will attempt to install the bouncycastle provider + // under jdk 1.3.1) + ConfigurationLoader.initialize(false); + BufferedReader reader = + new BufferedReader(new InputStreamReader(System.in)); + System.out.print("Connect to host? "); + String hostname = reader.readLine(); + // Make a client connection + SshClient ssh = new SshClient(); + ssh.setSocketTimeout(30000); + SshConnectionProperties properties = new SshConnectionProperties(); + properties.setHost(hostname); + properties.setPrefPublicKey("ssh-dss"); + // Connect to the host + ssh.connect(properties); + // Create a password authentication instance + PasswordAuthenticationClient pwd = new PasswordAuthenticationClient(); + // Get the users name + System.out.print("Username? "); + // Read the password + String username = reader.readLine(); + pwd.setUsername(username); + // Get the password + System.out.print("Password? "); + String password = reader.readLine(); + pwd.setPassword(password); + // Try the authentication + int result = ssh.authenticate(pwd); + // Evaluate the result + if (result == AuthenticationProtocolState.COMPLETE) { + // The connection is authenticated we can now do some real work! + SessionChannelClient session = ssh.openSessionChannel(); + if(!session.requestPseudoTerminal("vt100", 80, 24, 0, 0, "")) + System.out.println("Failed to allocate a pseudo terminal"); + if (session.startShell()) { + IOStreamConnector input = + new IOStreamConnector(); + IOStreamConnector output = + new IOStreamConnector(); + IOStreamConnector error = + new IOStreamConnector(); + output.setCloseOutput(false); + input.setCloseInput(false); + error.setCloseOutput(false); + input.connect(System.in, session.getOutputStream()); + output.connect(session.getInputStream(), System.out); + error.connect(session.getStderrInputStream(), System.out); + session.getState().waitForState(ChannelState.CHANNEL_CLOSED); + }else + System.out.println("Failed to start the users shell"); + ssh.disconnect(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/examples/PortForwarding.java b/examples/PortForwarding.java new file mode 100644 index 0000000000000000000000000000000000000000..af653f2df8bd4daf0db53554961a8bba11be830e --- /dev/null +++ b/examples/PortForwarding.java @@ -0,0 +1,100 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import java.io.BufferedReader; +import java.io.InputStreamReader; +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient; +import com.sshtools.j2ssh.forwarding.ForwardingClient; +import com.sshtools.j2ssh.transport.TransportProtocolState; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +/*import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter;*/ +/** + * Demonstrates a starting both a local and remote forwarding configuration. + * + * @author <A HREF="mailto:lee@sshtools.com">Lee David Painter</A> + * @version $Id: PortForwarding.java,v 1.9 2003/07/16 10:42:07 t_magicthize Exp $ + * + * @created 20 December 2002 + */ +public class PortForwarding { + /** + * The main program for the PortForwarding class + * + * @param args The command line arguments + */ + public static void main(String args[]) { + try { + // Setup a logfile + /*Handler fh = new FileHandler("example.log"); + fh.setFormatter(new SimpleFormatter()); + Logger.getLogger("com.sshtools").setUseParentHandlers(false); + Logger.getLogger("com.sshtools").addHandler(fh); + Logger.getLogger("com.sshtools").setLevel(Level.ALL);*/ + // Configure J2SSH (This will attempt to install the bouncycastle provider + // under jdk 1.3.1) + ConfigurationLoader.initialize(false); + BufferedReader reader = + new BufferedReader(new InputStreamReader(System.in)); + System.out.print("Connect to host? "); + String hostname = reader.readLine(); + // Make a client connection + SshClient ssh = new SshClient(); + // Connect to the hos + ssh.connect(hostname); + // Create a password authentication instance + PasswordAuthenticationClient pwd = new PasswordAuthenticationClient(); + // Get the users name + System.out.print("Username? "); + String username = reader.readLine(); + pwd.setUsername(username); + // Get the password + System.out.print("Password? "); + String password = reader.readLine(); + pwd.setPassword(password); + // Try the authentication + int result = ssh.authenticate(pwd); + // Evaluate the result + if (result == AuthenticationProtocolState.COMPLETE) { + ForwardingClient forwarding = ssh.getForwardingClient(); + forwarding.addLocalForwarding("Test Local", "0.0.0.0", 8081, + "127.0.0.1", 80); + forwarding.startLocalForwarding("Test Local"); + forwarding.addRemoteForwarding("Test Remote", "0.0.0.0", 8081, + "127.0.0.1", 8080); + forwarding.startRemoteForwarding("Test Remote"); + } + ssh.getConnectionState().waitForState(TransportProtocolState.DISCONNECTED); + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/examples/PublicKeyConnect.java b/examples/PublicKeyConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..dc1f9d51b7eca71759ae295ef8c764b530e130ea --- /dev/null +++ b/examples/PublicKeyConnect.java @@ -0,0 +1,129 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient; +import com.sshtools.j2ssh.io.IOStreamConnector; +import com.sshtools.j2ssh.io.IOStreamConnectorState; +import com.sshtools.j2ssh.session.SessionChannelClient; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKeyFile; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +/*import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter;*/ +/** + * Demonstrates a public key authentication connection to an SSH server. + * + * @author <A HREF="mailto:lee@sshtools.com">Lee David Painter</A> + * @version $Id: PublicKeyConnect.java,v 1.8 2003/07/16 10:42:08 t_magicthize Exp $ + * + * @created 20 December 2002 + */ +public class PublicKeyConnect { + /** + * The main program for the PublicKeyConnect class + * + * @param args The command line arguments + */ + public static void main(String args[]) { + try { + // Setup a logfile + /*Handler fh = new FileHandler("example.log"); + fh.setFormatter(new SimpleFormatter()); + Logger.getLogger("com.sshtools").setUseParentHandlers(false); + Logger.getLogger("com.sshtools").addHandler(fh); + Logger.getLogger("com.sshtools").setLevel(Level.ALL);*/ + // Configure J2SSH (This will attempt to install the bouncycastle provider + // under jdk 1.3.1) + ConfigurationLoader.initialize(false); + BufferedReader reader = + new BufferedReader(new InputStreamReader(System.in)); + System.out.print("Connect to host? "); + String hostname = reader.readLine(); + // Make a client connection + SshClient ssh = new SshClient(); + // Connect to the host + ssh.connect(hostname); + PublicKeyAuthenticationClient pk = new PublicKeyAuthenticationClient(); + // Get the users name + System.out.print("Username? "); + String username = reader.readLine(); + pk.setUsername(username); + // Get the private key file + System.out.print("Path to private key file? "); + String filename = reader.readLine(); + // Open up the private key file + SshPrivateKeyFile file = + SshPrivateKeyFile.parse(new File(filename)); + // If the private key is passphrase protected then ask for the passphrase + String passphrase = null; + if (file.isPassphraseProtected()) { + System.out.print("Enter passphrase? "); + passphrase = reader.readLine(); + } + // Get the key + SshPrivateKey key = file.toPrivateKey(passphrase); + pk.setKey(key); + // Try the authentication + int result = ssh.authenticate(pk); + // Evaluate the result + if (result == AuthenticationProtocolState.COMPLETE) { + // The connection is authenticated we can now do some real work! + SessionChannelClient session = ssh.openSessionChannel(); + if(!session.requestPseudoTerminal("vt100", 80, 24, 0, 0, "")) + System.out.println("Failed to allocate a pseudo terminal"); + if(session.startShell()) { + InputStream in = session.getInputStream(); + OutputStream out = session.getOutputStream(); + IOStreamConnector input = + new IOStreamConnector(System.in, session.getOutputStream()); + IOStreamConnector output = + new IOStreamConnector(session.getInputStream(), System.out); + output.getState().waitForState(IOStreamConnectorState.CLOSED); + } else + System.out.println("Failed to start the users shell"); + ssh.disconnect(); + } + if (result == AuthenticationProtocolState.PARTIAL) { + System.out.println("Further authentication requried!"); + } + if (result == AuthenticationProtocolState.FAILED) { + System.out.println("Authentication failed!"); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/examples/SftpConnect.java b/examples/SftpConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..5c68dcc4f68ca9b09d8ad4cabb2ef5003dd52652 --- /dev/null +++ b/examples/SftpConnect.java @@ -0,0 +1,125 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import java.io.BufferedReader; +import java.io.InputStreamReader; +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.session.SessionChannelClient; +import com.sshtools.j2ssh.sftp.FileAttributes; +import com.sshtools.j2ssh.sftp.SftpFile; +import com.sshtools.j2ssh.sftp.SftpFileOutputStream; +import com.sshtools.j2ssh.SftpClient; +import java.io.*; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +/*import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter;*/ +/** + * Demonstrates a simple password connection to an SSH server. + * + * @author <A HREF="mailto:lee@sshtools.com">Lee David Painter</A> + * @version $Id: SftpConnect.java,v 1.8 2003/07/16 10:42:08 t_magicthize Exp $ + * + * @created 20 December 2002 + */ +public class SftpConnect { + /** + * The main program for the PasswordConnect class + * + * @param args The command line arguments + */ + public static void main(String args[]) { + try { + // Setup a logfile + /*Handler fh = new FileHandler("example.log"); + fh.setFormatter(new SimpleFormatter()); + Logger.getLogger("com.sshtools").setUseParentHandlers(false); + Logger.getLogger("com.sshtools").addHandler(fh); + Logger.getLogger("com.sshtools").setLevel(Level.ALL);*/ + // Configure J2SSH (This will attempt to install the bouncycastle provider + // under jdk 1.3.1) + ConfigurationLoader.initialize(false); + BufferedReader reader = + new BufferedReader(new InputStreamReader(System.in)); + System.out.print("Connect to host? "); + String hostname = reader.readLine(); + // Make a client connection + SshClient ssh = new SshClient(); + // Connect to the host + ssh.connect(hostname); + // Create a password authentication instance + PasswordAuthenticationClient pwd = new PasswordAuthenticationClient(); + // Get the users name + System.out.print("Username? "); + String username = reader.readLine(); + pwd.setUsername(username); + // Get the password + System.out.print("Password? "); + String password = reader.readLine(); + pwd.setPassword(password); + // Try the authentication + int result = ssh.authenticate(pwd); + // Evaluate the result + if (result == AuthenticationProtocolState.COMPLETE) { + // The connection is authenticated we can now do some real work! + SftpClient sftp = ssh.openSftpClient(); + // Make a directory + try { + sftp.mkdir("j2ssh"); + } + catch (IOException ex) { + } + // Change directory + sftp.cd("j2ssh"); + System.out.println(sftp.pwd()); + // Change the mode + sftp.chmod(0777, "j2ssh"); + sftp.lcd("c:/"); + // Upload a file + sftp.put("system.gif"); + // Change the local directory + sftp.lcd("localdir"); + // Download a file + sftp.get("somefile.txt", "anotherfile.txt"); + // Remove a directory or file + sftp.rm("j2ssh"); + // Quit + sftp.quit(); + ssh.disconnect(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + System.exit(0); + } + } +} diff --git a/j2ssh.properties b/j2ssh.properties new file mode 100644 index 0000000000000000000000000000000000000000..a55b44acc7ed4eb9043264ace46494decf642ed1 --- /dev/null +++ b/j2ssh.properties @@ -0,0 +1,10 @@ +# J2SSH Project version +j2ssh.project.name=j2ssh +j2ssh.project.type= +j2ssh.version.major=0 +j2ssh.version.minor=2 +j2ssh.version.build=9 +core.project.name=core +common.project.name=common +sshant.project.name=ant +daemon.project.name=daemon diff --git a/src/com/sshtools/ant/ConditionalTasks.java b/src/com/sshtools/ant/ConditionalTasks.java new file mode 100644 index 0000000000000000000000000000000000000000..33d9f2405325e075cc63c288de72ee15aaf8a239 --- /dev/null +++ b/src/com/sshtools/ant/ConditionalTasks.java @@ -0,0 +1,152 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.ant; + +import org.apache.tools.ant.*; + +import java.io.*; + +import java.util.*; + + +public class ConditionalTasks extends Task implements TaskContainer { + private ArrayList tasks = new ArrayList(); + private String dirs; + private String files; + private String name; + private String family; + + public ConditionalTasks() { + } + + public void setFamily(String family) { + this.family = family; + } + + public void setDirs(String dirs) { + this.dirs = dirs; + } + + public void setFiles(String files) { + this.files = files; + } + + public void setName(String name) { + this.name = name; + } + + public void addTask(Task task) { + tasks.add(tasks.size(), task); + } + + public void execute() { + if ((dirs == null) && (files == null)) { + throw new BuildException( + "ConditionalTasks: You must supply at least one of either the files or dirs properties"); + } + + if (name == null) { + throw new BuildException( + "ConditionalTasks: You must supply a name for these conditional tasks!"); + } + + log("Verifying conditions for " + name); + + if (family != null) { + StringTokenizer tokenizer = new StringTokenizer(dirs, ","); + boolean familyMatch = false; + + while (tokenizer.hasMoreElements() && !familyMatch) { + String condition = (String) tokenizer.nextElement(); + + if (condition.equals(family)) { + familyMatch = true; + } + } + + if (!familyMatch) { + log("ConditionalTasks: OS Family '" + family + + "' does not match; " + name + " will not be performed"); + + return; + } + } + + File basedir = getProject().getBaseDir(); + + if (dirs != null) { + StringTokenizer tokenizer = new StringTokenizer(dirs, ","); + File f; + + while (tokenizer.hasMoreElements()) { + String condition = (String) tokenizer.nextElement(); + f = new File(basedir, condition); + + if (!f.exists()) { + f = new File(condition); + + if (!f.exists()) { + log("ConditionalTasks: Directory '" + condition + + "' does not exist; " + name + + " will not be performed"); + + return; + } + } + } + } + + if (files != null) { + StringTokenizer tokenizer = new StringTokenizer(files, ","); + File f; + + while (tokenizer.hasMoreElements()) { + String condition = (String) tokenizer.nextElement(); + f = new File(basedir, condition); + + if (!f.exists()) { + log("ConditionalTasks: File '" + condition + + "' does not exist; " + name + " will not be performed"); + + return; + } + } + } + + System.out.println("Executing Conditional Tasks"); + + Iterator it = tasks.iterator(); + Task task; + + while (it.hasNext()) { + task = (Task) it.next(); + task.setProject(getProject()); + task.setOwningTarget(getOwningTarget()); + task.setLocation(getLocation()); + task.perform(); + } + } +} diff --git a/src/com/sshtools/ant/Sftp.java b/src/com/sshtools/ant/Sftp.java new file mode 100644 index 0000000000000000000000000000000000000000..bb98c6e18fcccb57ece6bf0f2bcf32bc86f6370d --- /dev/null +++ b/src/com/sshtools/ant/Sftp.java @@ -0,0 +1,728 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.ant; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.sftp.*; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import org.apache.tools.ant.util.*; + +import java.io.*; + +import java.util.*; + + +/** + * Basic SFTP client. Performs the following actions: + * <ul> + * <li> <strong>put</strong> - send files to a remote server. This is the + * default action.</li> + * <li> <strong>get</strong> - retreive files from a remote server.</li> + * <li> <strong>del</strong> - delete files from a remote server.</li> + * <li> <strong>chmod</strong> - changes the mode of files on a remote server.</li> + * </ul> + * + */ +public class Sftp extends SshSubTask { + protected static final int SEND_FILES = 0; + protected static final int GET_FILES = 1; + protected static final int DEL_FILES = 2; + protected static final int MK_DIR = 4; + protected static final int CHMOD = 5; + + //private Ssh parent = null; + protected static final String[] ACTION_STRS = { + "Sending", "Getting", "Deleting", "Listing", "Making directory", "chmod" + }; + protected static final String[] COMPLETED_ACTION_STRS = { + "Sent", "Retrieved", "Deleted", "Listed", "Created directory", + "Mode changed" + }; + private String remotedir = "."; + + // private File listing; + private boolean verbose = false; + private boolean newerOnly = false; + private int action = SEND_FILES; + private Vector filesets = new Vector(); + private Vector dirCache = new Vector(); + private int transferred = 0; + private String remoteFileSep = "/"; + private boolean skipFailedTransfers = false; + private int skipped = 0; + private boolean ignoreNoncriticalErrors = false; + private String chmod = "777"; + private FileUtils fileUtils = FileUtils.newFileUtils(); + + /** + * Set to true to receive notification about each file as it is + * transferred. + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + /** + * Sets the remote working directory + * */ + public void setRemotedir(String remotedir) { + this.remotedir = remotedir; + } + + /** + * A synonym for <tt>depends</tt>. Set to true to transmit only new or changed + * files. + */ + public void setNewer(boolean newer) { + this.newerOnly = newer; + } + + /** + * Set to true to transmit only files that are new or changed from their + * remote counterparts. The default is to transmit all files. + */ + public void setDepends(boolean depends) { + this.newerOnly = depends; + } + + /** + * Sets the file permission mode (Unix only) for files sent to the server. + */ + public void setChmod(String theMode) { + this.chmod = theMode; + } + + /** + * A set of files to upload or download + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + /** + * Sets the FTP action to be taken. Currently accepts "put", "get", "del", + * "mkdir" and "list". + * + * @deprecated setAction(String) is deprecated and is replaced with + * setAction(FTP.Action) to make Ant's Introspection mechanism do the + * work and also to encapsulate operations on the type in its own + * class. + * @ant.attribute ignore="true" + */ + + /* public void setAction(String action) throws BuildException { + log("DEPRECATED - The setAction(String) method has been deprecated." + + " Use setAction(FTP.Action) instead."); + Action a = new Action(); + a.setValue(action); + this.action = a.getAction(); + }*/ + + /** + * Sets the FTP action to be taken. Currently accepts "put", "get", "del", + * "mkdir", "chmod" and "list". + */ + public void setAction(Action action) throws BuildException { + this.action = action.getAction(); + } + + /** + * If true, enables unsuccessful file put, delete and get + * operations to be skipped with a warning and the remainder + * of the files still transferred. + */ + public void setSkipFailedTransfers(boolean skipFailedTransfers) { + this.skipFailedTransfers = skipFailedTransfers; + } + + /** + * set the flag to skip errors on directory creation. + * (and maybe later other server specific errors) + */ + public void setIgnoreNoncriticalErrors(boolean ignoreNoncriticalErrors) { + this.ignoreNoncriticalErrors = ignoreNoncriticalErrors; + } + + /** Checks to see that all required parameters are set. */ + protected void checkConfiguration() throws BuildException { + /* if ( (action == LIST_FILES) && (listing == null)) { + throw new BuildException("listing attribute must be set for list " + + "action!"); + }*/ + if ((action == MK_DIR) && (remotedir == null)) { + throw new BuildException("remotedir attribute must be set for " + + "mkdir action!"); + } + + if ((action == CHMOD) && (chmod == null)) { + throw new BuildException("chmod attribute must be set for chmod " + + "action!"); + } + } + + /** + * For each file in the fileset, do the appropriate action: send, get, + * delete, or list. + */ + protected int transferFiles(SftpClient sftp, FileSet fs) + throws IOException, BuildException { + FileScanner ds; + + if (action == SEND_FILES) { + ds = fs.getDirectoryScanner(parent.getProject()); + } else { + ds = new SftpDirectoryScanner(sftp); + fs.setupDirectoryScanner(ds, parent.getProject()); + ds.scan(); + } + + String[] dsfiles = ds.getIncludedFiles(); + String dir = null; + + if ((ds.getBasedir() == null) && + ((action == SEND_FILES) || (action == GET_FILES))) { + throw new BuildException("the dir attribute must be set for send " + + "and get actions"); + } else { + if ((action == SEND_FILES) || (action == GET_FILES)) { + dir = ds.getBasedir().getAbsolutePath(); + } + } + + // If we are doing a listing, we need the output stream created now. + BufferedWriter bw = null; + + try { + /*if (action == LIST_FILES) { + File pd = fileUtils.getParentFile(listing); + if (!pd.exists()) { + pd.mkdirs(); + } + bw = new BufferedWriter(new FileWriter(listing)); + }*/ + for (int i = 0; i < dsfiles.length; i++) { + switch (action) { + case SEND_FILES: { + sendFile(sftp, dir, dsfiles[i]); + + break; + } + + case GET_FILES: { + getFile(sftp, dir, dsfiles[i]); + + break; + } + + case DEL_FILES: { + delFile(sftp, dsfiles[i]); + + break; + } + + case CHMOD: { + chmod(sftp, dsfiles[i]); + transferred++; + + break; + } + + default:throw new BuildException("unknown ftp action " + + action); + } + } + } finally { + if (bw != null) { + bw.close(); + } + } + + return dsfiles.length; + } + + /** + * Sends all files specified by the configured filesets to the remote + * server. + */ + protected void transferFiles(SftpClient sftp) + throws IOException, BuildException { + transferred = 0; + skipped = 0; + + if (filesets.size() == 0) { + throw new BuildException("at least one fileset must be specified."); + } else { + // get files from filesets + for (int i = 0; i < filesets.size(); i++) { + FileSet fs = (FileSet) filesets.elementAt(i); + + if (fs != null) { + transferFiles(sftp, fs); + } + } + } + + log(transferred + " files " + COMPLETED_ACTION_STRS[action]); + + if (skipped != 0) { + log(skipped + " files were not successfully " + + COMPLETED_ACTION_STRS[action]); + } + } + + /** + * Correct a file path to correspond to the remote host requirements. This + * implementation currently assumes that the remote end can handle + * Unix-style paths with forward-slash separators. This can be overridden + * with the <code>separator</code> task parameter. No attempt is made to + * determine what syntax is appropriate for the remote host. + */ + protected String resolveFile(String file) { + return file.replace(System.getProperty("file.separator").charAt(0), + remoteFileSep.charAt(0)); + } + + /** + * Creates all parent directories specified in a complete relative + * pathname. Attempts to create existing directories will not cause + * errors. + */ + protected void createParents(SftpClient sftp, String filename) + throws IOException, BuildException { + Vector parents = new Vector(); + File dir = new File(filename); + String dirname; + + while ((dirname = dir.getParent()) != null) { + dir = new File(dirname); + parents.addElement(dir); + } + + for (int i = parents.size() - 1; i >= 0; i--) { + dir = (File) parents.elementAt(i); + + if (!dirCache.contains(dir)) { + log("creating remote directory " + resolveFile(dir.getPath()), + Project.MSG_VERBOSE); + + try { + sftp.mkdir(resolveFile(dir.getPath())); + } catch (IOException ex) { + } + + dirCache.addElement(dir); + } + } + } + + /** + * Checks to see if the remote file is current as compared with the local + * file. Returns true if the remote file is up to date. + */ + protected boolean isUpToDate(SftpClient sftp, File localFile, + String remoteFile) throws IOException, BuildException { + try { + log("Checking date for " + remoteFile, Project.MSG_VERBOSE); + + FileAttributes attrs = sftp.stat(remoteFile); + + // SFTP uses seconds since Jan 1 1970 UTC + long remoteTimestamp = attrs.getModifiedTime().longValue() * 1000; //files[0].getTimestamp().getTime().getTime(); + + // Java uses milliseconds since Jan 1 1970 UTC + long localTimestamp = localFile.lastModified(); + + if (this.action == SEND_FILES) { + return remoteTimestamp > localTimestamp; + } else { + return localTimestamp > remoteTimestamp; + } + } catch (IOException ex) { + return false; + } + } + + /** + * Sends a single file to the remote host. <code>filename</code> may + * contain a relative path specification. When this is the case, <code>sendFile</code> + * will attempt to create any necessary parent directories before sending + * the file. The file will then be sent using the entire relative path + * spec - no attempt is made to change directories. It is anticipated that + * this may eventually cause problems with some FTP servers, but it + * simplifies the coding. + */ + protected void sendFile(SftpClient sftp, String dir, String filename) + throws IOException, BuildException { + InputStream instream = null; + SftpFileOutputStream out = null; + + try { + File file = parent.getProject().resolveFile(new File(dir, filename).getPath()); + String remotefile = resolveFile(filename); + + if (newerOnly && isUpToDate(sftp, file, remotefile)) { + return; + } + + if (verbose) { + log("transferring " + file.getAbsolutePath() + " to " + + remotedir + remotefile); + } + + instream = new BufferedInputStream(new FileInputStream(file)); + createParents(sftp, filename); + sftp.put(file.getAbsolutePath(), remotefile); + + // Set the umask + sftp.chmod(Integer.parseInt(chmod, 8), remotefile); + log("File " + file.getAbsolutePath() + " copied to " + parent.host, + Project.MSG_VERBOSE); + transferred++; + } catch (IOException ex1) { + String s = "Could not put file: " + ex1.getMessage(); + + if (skipFailedTransfers == true) { + log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + } finally { + try { + if (instream != null) { + instream.close(); + } + } catch (IOException ex) { + // ignore it + } + + try { + if (out != null) { + out.close(); + } + } catch (IOException ex) { + } + } + } + + /** Delete a file from the remote host. */ + protected void delFile(SftpClient sftp, String filename) + throws IOException, BuildException { + if (verbose) { + log("deleting " + filename); + } + + try { + String remotefile = resolveFile(filename); + sftp.rm(remotefile); + log("File " + filename + " deleted from " + parent.host, + Project.MSG_VERBOSE); + transferred++; + } catch (IOException ex) { + String s = "could not delete file: " + ex.getMessage(); + + if (skipFailedTransfers == true) { + log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + } + } + + protected void chmod(SftpClient sftp, String filename) + throws IOException, BuildException { + sftp.chmod(Integer.parseInt(chmod, 8), resolveFile(filename)); + } + + /** + * Retrieve a single file to the remote host. <code>filename</code> may + * contain a relative path specification. <p> + * + * The file will then be retreived using the entire relative path spec - + * no attempt is made to change directories. It is anticipated that this + * may eventually cause problems with some FTP servers, but it simplifies + * the coding.</p> + */ + protected void getFile(SftpClient sftp, String dir, String filename) + throws IOException, BuildException { + try { + String localfile = filename; + + if (localfile.indexOf("/") >= 0) { + localfile = localfile.substring(filename.lastIndexOf("/")); + } + + File file = parent.getProject().resolveFile(new File(dir, localfile).getAbsolutePath()); + log(dir); + log(filename); + log(file.getAbsolutePath()); + + if (newerOnly && isUpToDate(sftp, file, resolveFile(filename))) { + return; + } + + if (verbose) { + log("transferring " + filename + " to " + + file.getAbsolutePath()); + } + + File pdir = fileUtils.getParentFile(file); + + if (!pdir.exists()) { + pdir.mkdirs(); + } + + //sftp.lcd(dir); + // Get the file + sftp.get(filename, file.getAbsolutePath()); + + if (verbose) { + log("File " + file.getAbsolutePath() + " copied from " + + parent.host); + } + + FileAttributes attrs = sftp.stat(filename); + file.setLastModified(attrs.getModifiedTime().longValue() * 1000); + transferred++; + } catch (IOException ioe) { + String s = "could not get file: " + ioe.getMessage(); + + if (skipFailedTransfers == true) { + log(s, Project.MSG_WARN); + skipped++; + } else { + throw new BuildException(s); + } + } + } + + /** + * Create the specified directory on the remote host. + * + * @param sftp The SFTP client connection + * @param dir The directory to create + */ + protected void makeRemoteDir(SftpClient sftp, String dir) + throws BuildException { + if (verbose) { + log("creating directory: " + dir); + } + + try { + sftp.mkdir(dir); + } catch (IOException ex) { + log(ex.getMessage()); + } + } + + /** Runs the task. */ + public void execute(SshClient ssh) throws BuildException { + try { + Integer.parseInt(chmod, 8); + } catch (NumberFormatException ex) { + throw new BuildException( + "chmod attribute format is incorrect, use octal number format i.e 0777"); + } + + executeSFTPTask(ssh); + } + + protected void executeSFTPTask(SshClient ssh) throws BuildException { + SftpClient sftp = null; + + try { + sftp = ssh.openSftpClient(); + + if (action == MK_DIR) { + makeRemoteDir(sftp, remotedir); + } else { + if (remotedir.trim().length() > 0) { + log("Setting the remote directory "); //, Project.MSG_VERBOSE); + sftp.cd(remotedir); + } + + // Get the absolute path of the remote directory + remotedir = sftp.pwd(); + log("Remote directory is " + remotedir); + + if (!remotedir.endsWith("/")) { + remotedir += "/"; + } + + log(ACTION_STRS[action] + " files"); + transferFiles(sftp); + } + } catch (IOException ex) { + throw new BuildException("error during SFTP transfer: " + ex); + } finally { + if ((sftp != null) && !sftp.isClosed()) { + try { + log("Quiting SFTP", Project.MSG_VERBOSE); + sftp.quit(); + } catch (IOException ex) { + // ignore it + } + } + } + } + + protected class SftpDirectoryScanner extends DirectoryScanner { + protected SftpClient sftp = null; + + public SftpDirectoryScanner(SftpClient sftp) { + super(); + this.sftp = sftp; + } + + public void scan() { + if (includes == null) { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + + if (excludes == null) { + excludes = new String[0]; + } + + filesIncluded = new Vector(); + filesNotIncluded = new Vector(); + filesExcluded = new Vector(); + dirsIncluded = new Vector(); + dirsNotIncluded = new Vector(); + dirsExcluded = new Vector(); + scandir(remotedir, true); + } + + protected void scandir(String dir, boolean fast) { + try { + List children = sftp.ls(dir); + + if (!dir.endsWith("/")) { + dir += "/"; + } + + Iterator it = children.iterator(); + + while (it.hasNext()) { + SftpFile file = (SftpFile) it.next(); + + if (!file.getFilename().equals(".") && + !file.getFilename().equals("..")) { + if (file.isDirectory()) { + String name = dir + file.getFilename(); + + if (isIncluded(name)) { + if (!isExcluded(name)) { + dirsIncluded.addElement(name); + + if (fast) { + scandir(dir + file.getFilename(), fast); + } + } else { + dirsExcluded.addElement(name); + + if (fast && couldHoldIncluded(name)) { + scandir(dir + file.getFilename(), fast); + } + } + } else { + dirsNotIncluded.addElement(name); + + if (fast && couldHoldIncluded(name)) { + scandir(dir + file.getFilename(), fast); + } + } + + if (!fast) { + scandir(dir + file.getFilename(), fast); + } + } else { + if (file.isFile()) { + String name = dir + file.getFilename(); + + if (isIncluded(name)) { + if (!isExcluded(name)) { + filesIncluded.addElement(name); + } else { + filesExcluded.addElement(name); + } + } else { + filesNotIncluded.addElement(name); + } + } + } + } + } + + //ftp.changeToParentDirectory(); + } catch (IOException e) { + throw new BuildException("Error while communicating with SFTP ", + e); + } + } + } + + /** + * an action to perform, one of + * "send", "put", "recv", "get", "del", "delete", "list", "mkdir", "chmod" + */ + public static class Action extends EnumeratedAttribute { + private static final String[] validActions = { + "send", "put", "recv", "get", "del", "delete", "list", "mkdir", + "chmod" + }; + + public String[] getValues() { + return validActions; + } + + public int getAction() { + String actionL = getValue().toLowerCase(Locale.US); + + if (actionL.equals("send") || actionL.equals("put")) { + return SEND_FILES; + } else if (actionL.equals("recv") || actionL.equals("get")) { + return GET_FILES; + } else if (actionL.equals("del") || actionL.equals("delete")) { + return DEL_FILES; + } + /*else if (actionL.equals("list")) { + return LIST_FILES; + }*/ + else if (actionL.equals("chmod")) { + return CHMOD; + } else if (actionL.equals("mkdir")) { + return MK_DIR; + } + + return SEND_FILES; + } + } +} diff --git a/src/com/sshtools/ant/Ssh.java b/src/com/sshtools/ant/Ssh.java new file mode 100644 index 0000000000000000000000000000000000000000..6991d09524ed02a6c0f924302915ba708206f9bf --- /dev/null +++ b/src/com/sshtools/ant/Ssh.java @@ -0,0 +1,610 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.ant; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.authentication.*; +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.session.*; +import com.sshtools.j2ssh.transport.*; +import com.sshtools.j2ssh.transport.cipher.*; +import com.sshtools.j2ssh.transport.hmac.*; +import com.sshtools.j2ssh.transport.publickey.*; + +import org.apache.tools.ant.*; + +import java.io.*; + +import java.util.*; + + +public class Ssh extends Task { + protected String host; + protected int port = 22; + protected String username; + protected String password; + protected String keyfile; + protected String passphrase; + protected String cipher; + protected String mac; + protected String fingerprint; + protected String logfile = null; + protected boolean verifyhost = true; + protected boolean always = false; + protected SshClient ssh; + protected Vector tasks = new Vector(); + protected String sshtoolsHome; + protected String newline = "\n"; + + public Ssh() { + super(); + } + + protected void validate() throws BuildException { + if (host == null) { + throw new BuildException("You must provide a host to connect to!"); + } + + if (username == null) { + throw new BuildException( + "You must supply a username for authentication!"); + } + + if ((password == null) && (keyfile == null)) { + throw new BuildException( + "You must supply either a password or keyfile/passphrase to authenticate!"); + } + + if (verifyhost && (fingerprint == null)) { + throw new BuildException( + "Public key fingerprint required to verify the host"); + } + } + + protected void connectAndAuthenticate() throws BuildException { + if (sshtoolsHome != null) { + System.setProperty("sshtools.home", sshtoolsHome); + } + + log("Initializing J2SSH"); + + try { + ConfigurationLoader.initialize(false); + log("Creating connection to " + host + ":" + String.valueOf(port)); + + if (ssh == null) { + ssh = new SshClient(); + + SshConnectionProperties properties = new SshConnectionProperties(); + properties.setHost(host); + properties.setPort(port); + properties.setUsername(username); + + if (cipher != null) { + if (SshCipherFactory.getSupportedCiphers().contains(cipher)) { + properties.setPrefSCEncryption(cipher); + properties.setPrefCSEncryption(cipher); + } else { + this.log(cipher + + " is not a supported cipher, using default " + + SshCipherFactory.getDefaultCipher()); + } + } + + if (mac != null) { + if (SshHmacFactory.getSupportedMacs().contains(mac)) { + properties.setPrefCSMac(mac); + properties.setPrefSCMac(mac); + } else { + this.log(mac + + " is not a supported mac, using default " + + SshHmacFactory.getDefaultHmac()); + } + } + + log("Connecting...."); + ssh.connect(properties, + new AbstractKnownHostsKeyVerification( + new File(System.getProperty("user.home"), + ".ssh" + File.separator + "known_hosts").getAbsolutePath()) { + public void onUnknownHost(String hostname, + SshPublicKey key) throws InvalidHostFileException { + if (Ssh.this.verifyhost) { + if (key.getFingerprint().equalsIgnoreCase(Ssh.this.fingerprint)) { + allowHost(hostname, key, always); + } + } else { + allowHost(hostname, key, always); + } + } + + public void onHostKeyMismatch(String hostname, + SshPublicKey allowed, SshPublicKey supplied) + throws InvalidHostFileException { + if (Ssh.this.verifyhost) { + if (supplied.getFingerprint().equalsIgnoreCase(Ssh.this.fingerprint)) { + allowHost(hostname, supplied, always); + } + } else { + allowHost(hostname, supplied, always); + } + } + + public void onDeniedHost(String host) { + log("The server host key is denied!"); + } + }); + + int result; + boolean authenticated = false; + log("Authenticating " + username); + + if (keyfile != null) { + log("Performing public key authentication"); + + PublicKeyAuthenticationClient pk = new PublicKeyAuthenticationClient(); + + // Open up the private key file + SshPrivateKeyFile file = SshPrivateKeyFile.parse(new File( + keyfile)); + + // If the private key is passphrase protected then ask for the passphrase + if (file.isPassphraseProtected() && (passphrase == null)) { + throw new BuildException( + "Private key file is passphrase protected, please supply a valid passphrase!"); + } + + // Get the key + SshPrivateKey key = file.toPrivateKey(passphrase); + pk.setUsername(username); + pk.setKey(key); + + // Try the authentication + result = ssh.authenticate(pk); + + if (result == AuthenticationProtocolState.COMPLETE) { + authenticated = true; + } else if (result == AuthenticationProtocolState.PARTIAL) { + log( + "Public key authentication completed, attempting password authentication"); + } else { + throw new BuildException( + "Public Key Authentication failed!"); + } + } + + if ((password != null) && (authenticated == false)) { + log("Performing password authentication"); + + PasswordAuthenticationClient pwd = new PasswordAuthenticationClient(); + pwd.setUsername(username); + pwd.setPassword(password); + result = ssh.authenticate(pwd); + + if (result == AuthenticationProtocolState.COMPLETE) { + log("Authentication complete"); + } else if (result == AuthenticationProtocolState.PARTIAL) { + throw new BuildException( + "Password Authentication succeeded but further authentication required!"); + } else { + throw new BuildException( + "Password Authentication failed!"); + } + } + } + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + protected void disconnect() throws BuildException { + try { + log("Disconnecting from " + host); + ssh.disconnect(); + } catch (Exception ex) { + throw new BuildException(ex); + } + } + + public void execute() throws org.apache.tools.ant.BuildException { + validate(); + connectAndAuthenticate(); + executeSubTasks(); + disconnect(); + } + + protected void executeSubTasks() throws BuildException { + Iterator it = tasks.iterator(); + SshSubTask task; + + while (it.hasNext()) { + task = (SshSubTask) it.next(); + task.setParent(this); + task.execute(ssh); + } + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setPort(int port) { + this.port = port; + } + + public void setNewline(String newline) { + this.newline = newline; + } + + public void setHost(String host) { + this.host = host; + } + + public void setKeyfile(String keyfile) { + this.keyfile = keyfile; + } + + public void setPassphrase(String passphrase) { + this.passphrase = passphrase; + } + + public void setCipher(String cipher) { + this.cipher = cipher; + } + + public void setMac(String mac) { + this.mac = mac; + } + + public void setLogfile(String logfile) { + this.logfile = logfile; + } + + public void setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + } + + public void setVerifyhost(boolean verifyhost) { + this.verifyhost = verifyhost; + } + + public void setAlways(boolean always) { + this.always = always; + } + + public void setSshtoolshome(String sshtoolsHome) { + this.sshtoolsHome = sshtoolsHome; + } + + protected boolean hasMoreSftpTasks() { + Iterator it = tasks.iterator(); + + while (it.hasNext()) { + if (it.next().getClass().equals(Sftp.class)) { + return true; + } + } + + return false; + } + + public SshSubTask createShell() { + SshSubTask task = new Shell(); + tasks.addElement(task); + + return task; + } + + public SshSubTask createExec() { + SshSubTask task = new Exec(); + tasks.addElement(task); + + return task; + } + + public SshSubTask createSftp() { + SshSubTask task = new Sftp(); + tasks.addElement(task); + + return task; + } + + public class Exec extends Shell { + private String cmd; + + public void execute(SshClient ssh) throws BuildException { + this.validate(); + + try { + log("Executing command " + cmd); + + // Create the session channel + SessionChannelClient session = ssh.openSessionChannel(); + output = new SessionOutputReader(session); + + // Allocate a pseudo terminal is one has been requested + allocatePseudoTerminal(session); + + // Execute the command + if (session.executeCommand(cmd)) { + performTasks(session); + } else { + throw new BuildException("The command failed to start"); + } + } catch (IOException ex) { + } + } + + public void setCmd(String cmd) { + this.cmd = cmd; + } + } + + public class Shell extends SshSubTask implements PseudoTerminal { + private String term = null; + private int cols = 80; + private int rows = 34; + private int width = 0; + private int height = 0; + private String terminalModes = ""; + private Vector commands = new Vector(); + private SessionChannelClient session; + protected SessionOutputReader output; + + public void execute(SshClient ssh) throws BuildException { + this.validate(); + + try { + // Create the session channel + session = ssh.openSessionChannel(); + + // Add an event listener so we can filter the output for our read commands + // This is much easier than reading from an InputStream that could potentailly block + output = new SessionOutputReader(session); + + // Allocate a pseudo terminal is one has been requested + allocatePseudoTerminal(session); + + // Start the shell + if (session.startShell()) { + performTasks(session); + } else { + throw new BuildException("The session failed to start"); + } + } catch (IOException ex) { + } + } + + protected void validate() throws BuildException { + if (ssh == null) { + throw new BuildException("Invalid SSH session"); + } + + if (!ssh.isConnected()) { + throw new BuildException("The SSH session is not connected"); + } + } + + protected void allocatePseudoTerminal(SessionChannelClient session) + throws BuildException { + try { + if (term != null) { + if (!session.requestPseudoTerminal(this)) { + throw new BuildException( + "The server failed to allocate a pseudo terminal"); + } + } + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + protected void performTasks(SessionChannelClient session) + throws BuildException { + if (commands.size() > 0) { + Iterator it = commands.iterator(); + Object obj; + + while (it.hasNext()) { + obj = it.next(); + + if (obj instanceof Write) { + ((Write) obj).execute(); + } else if (obj instanceof Read) { + ((Read) obj).execute(); + } else { + throw new BuildException("Unexpected shell operation " + + obj.toString()); + } + } + } else { + try { + output.echoLineByLineToClose(new SessionOutputEcho() { + public void echo(String echo) { + log(echo); + } + }); + } catch (InterruptedException ex) { + throw new BuildException(ex); + } + } + } + + public void setTerm(String term) { + this.term = term; + } + + public void setCols(int cols) { + this.cols = cols; + } + + public void setRows(int rows) { + this.rows = rows; + } + + /** + * PseduoTermainal interface + */ + public String getTerm() { + return term; + } + + public int getColumns() { + return cols; + } + + public int getRows() { + return rows; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public String getEncodedTerminalModes() { + return terminalModes; + } + + /** + * Reading/Writing to the session/command + */ + public Write createWrite() { + Write write = new Write(); + commands.add(write); + + return write; + } + + public Read createRead() { + Read read = new Read(); + commands.add(read); + + return read; + } + + public class Read { + protected String taskString = ""; + private int timeout = 0; + private boolean echo = true; + + public void execute() throws BuildException { + try { + output.markCurrentPosition(); + + if (output.waitForString(taskString, timeout, + new SessionOutputEcho() { + public void echo(String msg) { + if (echo) { + log(msg); + } + } + }) + ) { + } else { + throw new BuildException("Timeout waiting for string " + + taskString); + } + } catch (InterruptedException ex) { + throw new BuildException(ex); + } + } + + /** + * the message as nested text + */ + public void addText(String s) { + setString(Ssh.this.getProject().replaceProperties(s)); + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public void setEcho(boolean echo) { + this.echo = echo; + } + + /** + * the message as an attribute + */ + public void setString(String s) { + taskString += s; + } + } + + public class Write { + protected boolean echo = true; + protected String taskString = ""; + protected boolean newline = true; + + public void execute() throws BuildException { + try { + if (echo) { + log(taskString); + } + + session.getOutputStream().write(taskString.getBytes()); + + if (newline) { + session.getOutputStream().write(Ssh.this.newline.getBytes()); + } + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + /** + * the message as nested text + */ + public void addText(String s) { + setString(Ssh.this.getProject().replaceProperties(s)); + } + + /** + * the message as an attribute + */ + public void setString(String s) { + taskString += s; + } + + public void setEcho(boolean echo) { + this.echo = echo; + } + + public void setNewline(boolean newline) { + this.newline = newline; + } + } + } +} diff --git a/src/com/sshtools/ant/SshSubTask.java b/src/com/sshtools/ant/SshSubTask.java new file mode 100644 index 0000000000000000000000000000000000000000..e5e299f9b6fdd5a99c87e1b0092198f219f3f746 --- /dev/null +++ b/src/com/sshtools/ant/SshSubTask.java @@ -0,0 +1,53 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.ant; + +import com.sshtools.j2ssh.*; + +import org.apache.tools.ant.*; + + +public class SshSubTask { + protected String taskString = ""; + protected Ssh parent; + + protected void setParent(Ssh parent) { + this.parent = parent; + } + + protected void log(String msg) { + parent.log(msg); + } + + protected void log(String msg, int i) { + parent.log(msg, i); + } + + public void execute(SshClient ssh) throws BuildException { + throw new BuildException( + "Shouldn't be able instantiate an SshSubTask directly"); + } +} diff --git a/src/com/sshtools/common/authentication/AuthenticationDialog.java b/src/com/sshtools/common/authentication/AuthenticationDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..3dd96b19fd5f3766d9507163aad5d3820d6c284e --- /dev/null +++ b/src/com/sshtools/common/authentication/AuthenticationDialog.java @@ -0,0 +1,267 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.authentication; + +import com.sshtools.common.ui.IconWrapperPanel; +import com.sshtools.common.ui.ResourceIcon; +import com.sshtools.common.ui.SshToolsConnectionHostTab; +import com.sshtools.common.ui.UIUtil; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class AuthenticationDialog extends JDialog { + JList jListAuths = new JList(); + JLabel messageLabel = new JLabel(); + boolean cancelled = false; + + /** +* Creates a new AuthenticationDialog object. +*/ + public AuthenticationDialog() { + super((Frame) null, "Select Authentication Method(s)", true); + init(); + } + + /** +* Creates a new AuthenticationDialog object. +* +* @param frame +*/ + public AuthenticationDialog(Frame frame) { + super(frame, "Select Authentication Method(s)", true); + init(); + } + + /** +* Creates a new AuthenticationDialog object. +* +* @param dialog +*/ + public AuthenticationDialog(Dialog dialog) { + super(dialog, "Select Authentication Method(s)", true); + init(); + } + + void init() { + try { + jbInit(); + pack(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + private void setMethodList(java.util.List methods) { + jListAuths.setListData(methods.toArray()); + + if (methods.size() > 0) { + jListAuths.setSelectedIndex(0); + } + } + + void jbInit() throws Exception { + // + setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); + + // + messageLabel.setForeground(Color.red); + messageLabel.setHorizontalAlignment(JLabel.CENTER); + + // Create the list of available methods and put in in a scroll panel + jListAuths = new JList(); + jListAuths.setVisibleRowCount(5); + + JPanel listPanel = new JPanel(new GridLayout(1, 1)); + listPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + listPanel.add(new JScrollPane(jListAuths)); + + // Main panel + JPanel centerPanel = new JPanel(new BorderLayout()); + centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + centerPanel.add(new JLabel( + "Please select an authentication method(s) to continue."), + BorderLayout.NORTH); + centerPanel.add(listPanel, BorderLayout.CENTER); + + // Create the bottom button panel + JButton proceed = new JButton("Proceed"); + proceed.setMnemonic('p'); + proceed.setDefaultCapable(true); + proceed.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + // I presume this component **is** reused? + setVisible(false); + } + }); + getRootPane().setDefaultButton(proceed); + + JButton cancel = new JButton("Cancel"); + cancel.setMnemonic('c'); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + cancelled = true; + setVisible(false); + } + }); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + southPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + southPanel.add(cancel); + southPanel.add(proceed); + + // Create the center banner panel + IconWrapperPanel iconPanel = new IconWrapperPanel(new ResourceIcon( + SshToolsConnectionHostTab.AUTH_ICON), centerPanel); + iconPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // The main panel contains everything and is surrounded by a border + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(messageLabel, BorderLayout.NORTH); + mainPanel.add(iconPanel, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + + // Build the main panel + getContentPane().setLayout(new GridLayout(1, 1)); + getContentPane().add(mainPanel); + } + + /** +* +* +* @param parent +* @param support +* +* @return +*/ + public static java.util.List showAuthenticationDialog(Component parent, + java.util.List support) { + return showAuthenticationDialog(parent, support, null); + } + + /** +* +* +* @param parent +* @param support +* @param message +* +* @return +*/ + public static java.util.List showAuthenticationDialog(Component parent, + java.util.List support, String message) { + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + AuthenticationDialog dialog = null; + + if (w instanceof Frame) { + dialog = new AuthenticationDialog((Frame) w); + } else if (w instanceof Dialog) { + dialog = new AuthenticationDialog((Dialog) w); + } else { + dialog = new AuthenticationDialog(); + } + + UIUtil.positionComponent(SwingConstants.CENTER, dialog); + + return dialog.showAuthenticationMethods(support, message); + } + + /** +* +* +* @param supported +* @param message +* +* @return +*/ + public java.util.List showAuthenticationMethods(java.util.List supported, + String message) { + // Set the list + this.setMethodList(supported); + + // Show the dialog + UIUtil.positionComponent(SwingConstants.CENTER, this); + + if (message != null) { + messageLabel.setVisible(true); + messageLabel.setText(message); + } else { + messageLabel.setVisible(false); + } + + pack(); + toFront(); + setVisible(true); + + // Put the selected values into a new list and return + java.util.List list = new ArrayList(); + + if (!cancelled) { + Object[] methods = jListAuths.getSelectedValues(); + + if (methods != null) { + for (int i = 0; i < methods.length; i++) { + list.add(methods[i]); + } + } + } + + return list; + } + + void jButtonProceed_actionPerformed(ActionEvent e) { + setVisible(false); + } +} diff --git a/src/com/sshtools/common/authentication/BannerDialog.java b/src/com/sshtools/common/authentication/BannerDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..37d802960d432f01fc5611852738693fea85288a --- /dev/null +++ b/src/com/sshtools/common/authentication/BannerDialog.java @@ -0,0 +1,187 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.authentication; + +import com.sshtools.common.ui.IconWrapperPanel; +import com.sshtools.common.ui.ResourceIcon; +import com.sshtools.common.ui.UIUtil; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class BannerDialog extends JDialog { + // Statics + final static String BANNER_ICON = "largebanner.png"; + + // Private instance variables + private JTextArea text; + + /** +* Creates a new BannerDialog object. +* +* @param bannerText +*/ + public BannerDialog(String bannerText) { + super((Frame) null, "SSH Authentication - Banner Message", true); + init(bannerText); + } + + /** +* Creates a new BannerDialog object. +* +* @param parent +* @param bannerText +*/ + public BannerDialog(Frame parent, String bannerText) { + super(parent, "SSH Authentication - Banner Message", true); + init(bannerText); + } + + /** +* Creates a new BannerDialog object. +* +* @param parent +* @param bannerText +*/ + public BannerDialog(Dialog parent, String bannerText) { + super(parent, "SSH Authentication - Banner Message", true); + init(bannerText); + } + + void init(String bannerText) { + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + + // + setText(bannerText); + } + + /** +* +* +* @param parent +* @param bannerText +*/ + public static void showBannerDialog(Component parent, String bannerText) { + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + BannerDialog dialog = null; + + if (w instanceof Frame) { + dialog = new BannerDialog((Frame) w, bannerText); + } else if (w instanceof Dialog) { + dialog = new BannerDialog((Dialog) w, bannerText); + } else { + dialog = new BannerDialog(bannerText); + } + + UIUtil.positionComponent(SwingConstants.CENTER, dialog); + dialog.toFront(); + dialog.setVisible(true); + } + + /** +* +* +* @param text +*/ + public void setText(String text) { + this.text.setText(text); + this.repaint(); + } + + void jbInit() throws Exception { + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + + // Create the component to display the banner text + text = new JTextArea(); + text.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // text.setLineWrap(true); + text.setEditable(false); + + Font f = new Font("MonoSpaced", text.getFont().getStyle(), + text.getFont().getSize()); + text.setFont(f); + + JScrollPane textScroller = new JScrollPane(text); + + // Create the center banner panel + IconWrapperPanel centerPanel = new IconWrapperPanel(new ResourceIcon( + BannerDialog.class, BANNER_ICON), textScroller); + centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // Create the south button panel + JButton ok = new JButton("Ok"); + ok.setMnemonic('o'); + ok.setDefaultCapable(true); + ok.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + // I presume this component is not reused? + dispose(); + } + }); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + southPanel.add(ok); + getRootPane().setDefaultButton(ok); + + // Build the main panel + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(centerPanel, BorderLayout.CENTER); + getContentPane().add(southPanel, BorderLayout.SOUTH); + + // + setSize(500, 245); + setResizable(false); + } +} diff --git a/src/com/sshtools/common/authentication/KBIRequestHandlerDialog.java b/src/com/sshtools/common/authentication/KBIRequestHandlerDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..3e01954f7cefb8a4699d69c5218b2996ccf7682a --- /dev/null +++ b/src/com/sshtools/common/authentication/KBIRequestHandlerDialog.java @@ -0,0 +1,199 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.authentication; + +import com.sshtools.common.ui.IconWrapperPanel; +import com.sshtools.common.ui.ResourceIcon; +import com.sshtools.common.ui.UIUtil; +import com.sshtools.common.ui.XTextField; + +import com.sshtools.j2ssh.authentication.KBIPrompt; +import com.sshtools.j2ssh.authentication.KBIRequestHandler; + +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.SwingConstants; +import javax.swing.text.JTextComponent; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class KBIRequestHandlerDialog extends JDialog + implements KBIRequestHandler { + /** */ + public final static String KBI_ICON = "largekbi.png"; + boolean cancelled; + JLabel instructionLabel = new JLabel(); + JPanel buttonsPanel = new JPanel(); + JTextComponent[] promptReply; + + /** +* Creates a new KBIRequestHandlerDialog object. +*/ + public KBIRequestHandlerDialog() { + super((Frame) null, "", true); + init(); + } + + /** +* Creates a new KBIRequestHandlerDialog object. +* +* @param frame +*/ + public KBIRequestHandlerDialog(Frame frame) { + super(frame, "", true); + init(); + } + + /** +* Creates a new KBIRequestHandlerDialog object. +* +* @param dialog +*/ + public KBIRequestHandlerDialog(Dialog dialog) { + super(dialog, "", true); + init(); + } + + void init() { + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + instructionLabel.setHorizontalAlignment(JLabel.CENTER); + instructionLabel.setBorder(BorderFactory.createEmptyBorder(4, 4, 8, 4)); + + JButton ok = new JButton("Ok"); + ok.setMnemonic('o'); + ok.setDefaultCapable(true); + ok.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + setVisible(false); + } + }); + getRootPane().setDefaultButton(ok); + + JButton cancel = new JButton("Cancel"); + cancel.setMnemonic('c'); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + cancelled = true; + setVisible(false); + } + }); + buttonsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + buttonsPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + buttonsPanel.add(cancel); + buttonsPanel.add(ok); + } + + /** +* +* +* @param name +* @param instruction +* @param prompts +*/ + public void showPrompts(String name, String instruction, KBIPrompt[] prompts) { + setTitle(name); + getContentPane().invalidate(); + getContentPane().removeAll(); + instructionLabel.setText(instruction); + + JPanel promptPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(0, 0, 4, 4); + gbc.fill = GridBagConstraints.CENTER; + gbc.anchor = GridBagConstraints.WEST; + promptReply = new JTextComponent[prompts.length]; + + for (int i = 0; i < prompts.length; i++) { + if (prompts[i].echo()) { + promptReply[i] = new XTextField(prompts[i].getResponse(), 15); + } else { + promptReply[i] = new JPasswordField(prompts[i].getResponse(), 15); + } + + System.out.println("Creating prompt " + prompts[i].getPrompt() + + " and setting to " + prompts[i].getResponse()); + gbc.weightx = 0.0; + UIUtil.jGridBagAdd(promptPanel, + new JLabel(prompts[i].getPrompt() + " "), gbc, + GridBagConstraints.RELATIVE); + gbc.weightx = 1.0; + UIUtil.jGridBagAdd(promptPanel, promptReply[i], gbc, + GridBagConstraints.REMAINDER); + } + + JPanel centerPanel = new JPanel(new BorderLayout()); + centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + centerPanel.add(instructionLabel, BorderLayout.NORTH); + centerPanel.add(promptPanel, BorderLayout.CENTER); + + // Create the center banner panel + IconWrapperPanel iconPanel = new IconWrapperPanel(new ResourceIcon( + KBIRequestHandlerDialog.class, KBI_ICON), centerPanel); + iconPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // The main panel contains everything and is surrounded by a border + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(iconPanel, BorderLayout.CENTER); + mainPanel.add(buttonsPanel, BorderLayout.SOUTH); + + // Build the main panel + getContentPane().setLayout(new GridLayout(1, 1)); + getContentPane().add(mainPanel); + getContentPane().validate(); + pack(); + UIUtil.positionComponent(SwingConstants.CENTER, this); + setVisible(true); + + if (!cancelled) { + for (int i = 0; i < promptReply.length; i++) { + System.out.println("Setting reply " + i + " to " + + promptReply[i].getText()); + prompts[i].setResponse(promptReply[i].getText()); + } + } + } +} diff --git a/src/com/sshtools/common/authentication/PassphraseDialog.java b/src/com/sshtools/common/authentication/PassphraseDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..ebf65c7e2551456ee12d6e453157306c062aa615 --- /dev/null +++ b/src/com/sshtools/common/authentication/PassphraseDialog.java @@ -0,0 +1,249 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.authentication; + +import com.sshtools.common.ui.IconWrapperPanel; +import com.sshtools.common.ui.ResourceIcon; +import com.sshtools.common.ui.UIUtil; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class PassphraseDialog extends JDialog { + // Statics + final static String PASSPHRASE_ICON = "/com/sshtools/common/authentication/largepassphrase.png"; + JButton jButtonCancel = new JButton(); + JButton jButtonOK = new JButton(); + JLabel message = new JLabel("Enter passphrase"); + JPasswordField jPasswordField = new JPasswordField(20); + boolean userCancelled = false; + + /** +* Creates a new PassphraseDialog object. +*/ + public PassphraseDialog() { + super((Frame) null, "Passphrase", true); + init(null); + } + + /** +* Creates a new PassphraseDialog object. +* +* @param parent +*/ + public PassphraseDialog(Frame parent) { + super(parent, "Passphrase", true); + init(parent); + } + + /** +* Creates a new PassphraseDialog object. +* +* @param parent +* @param identity +*/ + public PassphraseDialog(Frame parent, String identity) { + super(parent, "Passphrase", true); + init(parent); + setTitle(identity + " - Identity"); + } + + /** +* Creates a new PassphraseDialog object. +* +* @param parent +*/ + public PassphraseDialog(Dialog parent) { + super(parent, "Passphrase", true); + init(parent); + } + + /*public void setVisible(boolean visible) { +if (visible) { +UIUtil.positionComponent(UIUtil.CENTER, PassphraseDialog.this); +} + }*/ + + /** +* +* +* @return +*/ + public boolean isCancelled() { + return userCancelled; + } + + /** +* +* +* @param message +*/ + public void setMessage(String message) { + this.message.setText(message); + } + + /** +* +* +* @param color +*/ + public void setMessageForeground(Color color) { + message.setForeground(color); + } + + /** +* +* +* @return +*/ + public char[] getPassphrase() { + return jPasswordField.getPassword(); + } + + void init(Window parent) { + getContentPane().setLayout(new GridLayout(1, 1)); + + if (parent != null) { + this.setLocationRelativeTo(parent); + } + + try { + jbInit(); + pack(); + UIUtil.positionComponent(UIUtil.CENTER, PassphraseDialog.this); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + void jButtonCancel_actionPerformed(ActionEvent e) { + userCancelled = true; + setVisible(false); + } + + void jButtonOK_actionPerformed(ActionEvent e) { + userCancelled = false; + setVisible(false); + } + + void jbInit() throws Exception { + // Add a window listener to see when the window closes without + // selecting OK + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + userCancelled = true; + } + }); + + // Ok button + jButtonOK.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + jButtonOK_actionPerformed(e); + } + }); + jButtonOK.setText("OK"); + jButtonOK.setMnemonic('o'); + getRootPane().setDefaultButton(jButtonOK); + + // Cancel button + jButtonCancel.setText("Cancel"); + jButtonCancel.setMnemonic('c'); + jButtonCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + jButtonCancel_actionPerformed(e); + } + }); + + // Passphrase panel + JPanel passphrasePanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 2, 2, 2); + gbc.weightx = 1.0; + UIUtil.jGridBagAdd(passphrasePanel, message, gbc, + GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(passphrasePanel, jPasswordField, gbc, + GridBagConstraints.REMAINDER); + + // Create the center banner panel + IconWrapperPanel centerPanel = new IconWrapperPanel(new ResourceIcon( + PASSPHRASE_ICON), passphrasePanel); + centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // + JPanel buttonPanel = new JPanel(new GridBagLayout()); + gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(6, 6, 0, 0); + gbc.weighty = 1.0; + UIUtil.jGridBagAdd(buttonPanel, jButtonOK, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(buttonPanel, jButtonCancel, gbc, + GridBagConstraints.REMAINDER); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + southPanel.add(buttonPanel); + + // Wrap the whole thing in an empty border + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(centerPanel, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + + // Build the main panel + getContentPane().add(mainPanel); + + // + jPasswordField.grabFocus(); + } +} diff --git a/src/com/sshtools/common/authentication/PasswordAuthenticationDialog.java b/src/com/sshtools/common/authentication/PasswordAuthenticationDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..dc6cb06faa8babb6b1952ba2d6622ec10f90dc16 --- /dev/null +++ b/src/com/sshtools/common/authentication/PasswordAuthenticationDialog.java @@ -0,0 +1,313 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.authentication; + +import com.sshtools.common.ui.IconWrapperPanel; +import com.sshtools.common.ui.ResourceIcon; +import com.sshtools.common.ui.UIUtil; +import com.sshtools.common.ui.XTextField; + +import com.sshtools.j2ssh.authentication.AuthenticationProtocolException; +import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationPrompt; + +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import java.lang.reflect.Method; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.SwingConstants; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class PasswordAuthenticationDialog extends JDialog + implements SshAuthenticationPrompt { + // Statics + final static String KEY_ICON = "largecard.png"; + PasswordAuthenticationClient instance; + JButton jButtonCancel = new JButton(); + JButton jButtonOK = new JButton(); + JPasswordField jPasswordField = new JPasswordField(20); + XTextField jTextUsername = new XTextField(20); + boolean userCancelled = false; + + /** +* Creates a new PasswordAuthenticationDialog object. +*/ + public PasswordAuthenticationDialog() { + super((Frame) null, "Password Authentication", true); + init(null); + } + + /** +* Creates a new PasswordAuthenticationDialog object. +* +* @param parent +*/ + public PasswordAuthenticationDialog(Frame parent) { + super(parent, "Password Authentication", true); + init(parent); + } + + /** +* Creates a new PasswordAuthenticationDialog object. +* +* @param parent +*/ + public PasswordAuthenticationDialog(Dialog parent) { + super(parent, "Password Authentication", true); + init(parent); + } + + /** +* +* +* @return +*/ + public String getPassword() { + return String.valueOf(jPasswordField.getPassword()); + } + + /** +* +* +* @return +*/ + public String getUsername() { + return jTextUsername.getText(); + } + + /** +* +* +* @param username +*/ + public void setUsername(String username) { + if (username != null) { + jTextUsername.setText(username); + } + } + + /** +* +* +* @param instance +* +* @throws AuthenticationProtocolException +*/ + public void setInstance(SshAuthenticationClient instance) + throws AuthenticationProtocolException { + if (instance instanceof PasswordAuthenticationClient) { + this.instance = (PasswordAuthenticationClient) instance; + } else { + throw new AuthenticationProtocolException( + "PasswordAuthenticationClient instance required"); + } + } + + /** +* +* +* @return +*/ + public boolean showPrompt(SshAuthenticationClient inst) + throws AuthenticationProtocolException { + if (inst instanceof PasswordAuthenticationClient) { + instance = (PasswordAuthenticationClient) inst; + + if (instance.getUsername() != null) { + jTextUsername.setText(instance.getUsername()); + } + + if (!jTextUsername.getText().equals("")) { + jPasswordField.grabFocus(); + } + + UIUtil.positionComponent(SwingConstants.CENTER, this); + setVisible(true); + + if (!userCancelled) { + instance.setUsername(getUsername()); + instance.setPassword(getPassword()); + + return true; + } else { + return false; + } + } else { + throw new AuthenticationProtocolException( + "PasswordAuthenticationClient instance required"); + } + } + + void init(Window parent) { + setModal(true); + + getContentPane().setLayout(new GridLayout(1, 1)); + + if (parent != null) { + try { + Method m = getClass().getMethod("setLocationRelativeTo", + new Class[] { parent.getClass() }); + m.invoke(this, new Object[] { parent }); + } catch (Throwable t) { + // + } + } + + try { + jbInit(); + pack(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + void jButtonCancel_actionPerformed(ActionEvent e) { + userCancelled = true; + setVisible(false); + } + + void jButtonOK_actionPerformed(ActionEvent e) { + if (jTextUsername.getText().trim().equals("")) { + JOptionPane.showMessageDialog(this, "You must enter a username!", + "Password Authentication", JOptionPane.OK_OPTION); + + return; + } + + setVisible(false); + } + + void jbInit() throws Exception { + // Add a window listener to see when the window closes without + // selecting OK + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + userCancelled = true; + } + }); + + // Ok button + jButtonOK.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + jButtonOK_actionPerformed(e); + } + }); + jButtonOK.setText("OK"); + jButtonOK.setMnemonic('o'); + getRootPane().setDefaultButton(jButtonOK); + + // Cancel button + jButtonCancel.setText("Cancel"); + jButtonCancel.setMnemonic('c'); + jButtonCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + jButtonCancel_actionPerformed(e); + } + }); + + // User / password panel + JPanel userPasswordPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 2, 2, 2); + gbc.weightx = 1.0; + + // Username + UIUtil.jGridBagAdd(userPasswordPanel, new JLabel("User"), gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + UIUtil.jGridBagAdd(userPasswordPanel, jTextUsername, gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.NONE; + + // Username + UIUtil.jGridBagAdd(userPasswordPanel, new JLabel("Password"), gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + UIUtil.jGridBagAdd(userPasswordPanel, jPasswordField, gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.NONE; + + // Create the center banner panel + IconWrapperPanel centerPanel = new IconWrapperPanel(new ResourceIcon( + PasswordAuthenticationDialog.class, KEY_ICON), + userPasswordPanel); + centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // + JPanel buttonPanel = new JPanel(new GridBagLayout()); + gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(6, 6, 0, 0); + gbc.weighty = 1.0; + UIUtil.jGridBagAdd(buttonPanel, jButtonOK, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(buttonPanel, jButtonCancel, gbc, + GridBagConstraints.REMAINDER); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + southPanel.add(buttonPanel); + + // Wrap the whole thing in an empty border + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(centerPanel, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + + // Build the main panel + getContentPane().add(mainPanel); + + // + jPasswordField.grabFocus(); + } +} diff --git a/src/com/sshtools/common/authentication/PasswordChange.java b/src/com/sshtools/common/authentication/PasswordChange.java new file mode 100644 index 0000000000000000000000000000000000000000..cfb73dbface116f92669e0706f3ff904ea4ae8e9 --- /dev/null +++ b/src/com/sshtools/common/authentication/PasswordChange.java @@ -0,0 +1,234 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.authentication; + +import com.sshtools.common.ui.IconWrapperPanel; +import com.sshtools.common.ui.ResourceIcon; +import com.sshtools.common.ui.SshToolsConnectionHostTab; +import com.sshtools.common.ui.UIUtil; + +import com.sshtools.j2ssh.authentication.PasswordChangePrompt; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class PasswordChange implements PasswordChangePrompt { + // + + /** */ + public final static String PASSWORD_ICON = "/com/sshtools/common/authentication/largepassword.png"; + + // + private static PasswordChange instance; + + // + private Component parent; + + private PasswordChange() { + } + + /** +* +* +* @param parent +*/ + public void setParentComponent(Component parent) { + this.parent = parent; + } + + /** +* +* +* @param prompt +* +* @return +*/ + public String changePassword(String prompt) { + Window w = (parent == null) ? null + : (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + PasswordChangeDialog dialog = null; + + if (w instanceof Frame) { + dialog = new PasswordChangeDialog((Frame) w, prompt); + } else if (w instanceof Dialog) { + dialog = new PasswordChangeDialog((Dialog) w, prompt); + } else { + dialog = new PasswordChangeDialog(prompt); + } + + char[] p = dialog.getPassword(); + + return (p == null) ? null : new String(p); + } + + /** +* +* +* @return +*/ + public static PasswordChange getInstance() { + if (instance == null) { + instance = new PasswordChange(); + } + + return instance; + } + + class PasswordChangeDialog extends JDialog { + JLabel promptLabel = new JLabel(); + JPasswordField password = new JPasswordField(15); + JPasswordField confirm = new JPasswordField(15); + boolean cancelled; + + PasswordChangeDialog(String prompt) { + super((Frame) null, "Password Change", true); + init(prompt); + } + + PasswordChangeDialog(Frame frame, String prompt) { + super(frame, "Password Change", true); + init(prompt); + } + + PasswordChangeDialog(Dialog dialog, String prompt) { + super(dialog, "Password Change", true); + init(prompt); + } + + char[] getPassword() { + return (cancelled == true) ? null : password.getPassword(); + } + + void init(String prompt) { + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + + JPanel g = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(0, 0, 2, 2); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 0.0; + UIUtil.jGridBagAdd(g, new JLabel("Password: "), gbc, + GridBagConstraints.RELATIVE); + gbc.weightx = 1.0; + UIUtil.jGridBagAdd(g, password, gbc, GridBagConstraints.REMAINDER); + gbc.weightx = 0.0; + UIUtil.jGridBagAdd(g, new JLabel("Confirm: "), gbc, + GridBagConstraints.RELATIVE); + gbc.weightx = 1.0; + UIUtil.jGridBagAdd(g, confirm, gbc, GridBagConstraints.REMAINDER); + + // + promptLabel.setHorizontalAlignment(JLabel.CENTER); + + // Main panel + JPanel centerPanel = new JPanel(new BorderLayout()); + centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + centerPanel.add(promptLabel, BorderLayout.NORTH); + centerPanel.add(g, BorderLayout.CENTER); + + // Create the bottom button panel + JButton ok = new JButton("Ok"); + ok.setMnemonic('o'); + ok.setDefaultCapable(true); + ok.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (!new String(password.getPassword()).equals( + new String(confirm.getPassword()))) { + JOptionPane.showMessageDialog(PasswordChangeDialog.this, + "Passwords do not match. Please try again.", + "Passwords do not match", + JOptionPane.ERROR_MESSAGE); + } else { + setVisible(false); + } + } + }); + getRootPane().setDefaultButton(ok); + + JButton cancel = new JButton("Cancel"); + cancel.setMnemonic('c'); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + cancelled = true; + setVisible(false); + } + }); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + southPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + southPanel.add(cancel); + southPanel.add(ok); + + // Create the center banner panel + IconWrapperPanel iconPanel = new IconWrapperPanel(new ResourceIcon( + SshToolsConnectionHostTab.AUTH_ICON), centerPanel); + iconPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // The main panel contains everything and is surrounded by a border + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(iconPanel, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + + // Build the main panel + getContentPane().setLayout(new GridLayout(1, 1)); + getContentPane().add(mainPanel); + pack(); + toFront(); + UIUtil.positionComponent(SwingConstants.CENTER, this); + setVisible(true); + } + } +} diff --git a/src/com/sshtools/common/authentication/PublicKeyAuthenticationPrompt.java b/src/com/sshtools/common/authentication/PublicKeyAuthenticationPrompt.java new file mode 100644 index 0000000000000000000000000000000000000000..cfacff81cd58f089d369bcf7a28e8ba38bb1271c --- /dev/null +++ b/src/com/sshtools/common/authentication/PublicKeyAuthenticationPrompt.java @@ -0,0 +1,177 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.authentication; + +import com.sshtools.j2ssh.authentication.AuthenticationProtocolException; +import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationPrompt; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKeyFile; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Window; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class PublicKeyAuthenticationPrompt implements SshAuthenticationPrompt { + private Component parent; + private PublicKeyAuthenticationClient instance; + + /** +* Creates a new PublicKeyAuthenticationPrompt object. +* +* @param parent +*/ + public PublicKeyAuthenticationPrompt(Component parent) { + this.parent = parent; + } + + /** +* +* +* @param instance +* +* @throws AuthenticationProtocolException +*/ + public void setInstance(SshAuthenticationClient instance) + throws AuthenticationProtocolException { + if (instance instanceof PublicKeyAuthenticationClient) { + this.instance = (PublicKeyAuthenticationClient) instance; + } else { + throw new AuthenticationProtocolException( + "PublicKeyAuthenticationClient instance required"); + } + } + + /** +* +* +* @return +*/ + public boolean showPrompt(SshAuthenticationClient inst) + throws AuthenticationProtocolException { + if (inst instanceof PublicKeyAuthenticationClient) { + instance = (PublicKeyAuthenticationClient) inst; + } else { + throw new AuthenticationProtocolException( + "PublicKeyAuthenticationClient instance required"); + } + + File keyfile = (instance.getKeyfile() == null) ? null + : new File(instance.getKeyfile()); + String passphrase = null; + SshPrivateKeyFile pkf = null; + SshPrivateKey key; + + if ((keyfile == null) || !keyfile.exists()) { + JFileChooser chooser = new JFileChooser(); + chooser.setFileHidingEnabled(false); + chooser.setDialogTitle("Select Private Key File For Authentication"); + + if (chooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + keyfile = chooser.getSelectedFile(); + } else { + return false; + } + } + + FileInputStream in = null; + + try { + pkf = SshPrivateKeyFile.parse(keyfile); + } catch (InvalidSshKeyException iske) { + JOptionPane.showMessageDialog(parent, iske.getMessage()); + + return false; + } catch (IOException ioe) { + JOptionPane.showMessageDialog(parent, ioe.getMessage()); + } + + // Now see if its passphrase protected + if (pkf.isPassphraseProtected()) { + // Show the passphrase dialog + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + PassphraseDialog dialog = null; + + if (w instanceof Frame) { + dialog = new PassphraseDialog((Frame) w); + } else if (w instanceof Dialog) { + dialog = new PassphraseDialog((Dialog) w); + } else { + dialog = new PassphraseDialog(); + } + + do { + dialog.setVisible(true); + + if (dialog.isCancelled()) { + return false; + } + + passphrase = new String(dialog.getPassphrase()); + + try { + key = pkf.toPrivateKey(passphrase); + + break; + } catch (InvalidSshKeyException ihke) { + dialog.setMessage("Passphrase Invalid! Try again"); + dialog.setMessageForeground(Color.red); + } + } while (true); + } else { + try { + key = pkf.toPrivateKey(passphrase); + } catch (InvalidSshKeyException ihke) { + return false; + } + } + + instance.setKey(key); + instance.setKeyfile(keyfile.getAbsolutePath()); + + return true; + } +} diff --git a/src/com/sshtools/common/authentication/largebanner.png b/src/com/sshtools/common/authentication/largebanner.png new file mode 100644 index 0000000000000000000000000000000000000000..5842dca2ccf6adfd4100a29cb1622f1e44d451fd Binary files /dev/null and b/src/com/sshtools/common/authentication/largebanner.png differ diff --git a/src/com/sshtools/common/authentication/largecard.png b/src/com/sshtools/common/authentication/largecard.png new file mode 100644 index 0000000000000000000000000000000000000000..145059180d03429672479b24622ea4302b7df528 Binary files /dev/null and b/src/com/sshtools/common/authentication/largecard.png differ diff --git a/src/com/sshtools/common/authentication/largekbi.png b/src/com/sshtools/common/authentication/largekbi.png new file mode 100644 index 0000000000000000000000000000000000000000..877e309e62fdf10b90b4e141826722b93a26db4f Binary files /dev/null and b/src/com/sshtools/common/authentication/largekbi.png differ diff --git a/src/com/sshtools/common/authentication/largepassphrase.png b/src/com/sshtools/common/authentication/largepassphrase.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d2bb01b67f37b6d66b99e0c575d50cd4e152e3 Binary files /dev/null and b/src/com/sshtools/common/authentication/largepassphrase.png differ diff --git a/src/com/sshtools/common/authentication/largepassword.png b/src/com/sshtools/common/authentication/largepassword.png new file mode 100644 index 0000000000000000000000000000000000000000..811f0159842630389e19e156628421b1eb97c48c Binary files /dev/null and b/src/com/sshtools/common/authentication/largepassword.png differ diff --git a/src/com/sshtools/common/automate/AuthorizedKeys.java b/src/com/sshtools/common/automate/AuthorizedKeys.java new file mode 100644 index 0000000000000000000000000000000000000000..7329df129c9aea3b1ada7bcfa9fba604fa6ae830 --- /dev/null +++ b/src/com/sshtools/common/automate/AuthorizedKeys.java @@ -0,0 +1,140 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class AuthorizedKeys { + private HashMap keys = new HashMap(); + + /** +* +* +* @return +*/ + public Map getAuthorizedKeys() { + return keys; + } + + /** +* +* +* @param username +* @param key +*/ + public void addKey(String username, SshPublicKey key) { + if (!containsKey(key)) { + keys.put(key, username); + } + } + + /** +* +* +* @param key +*/ + public void removeKey(SshPublicKey key) { + keys.remove(key); + } + + /** +* +* +* @param key +* +* @return +*/ + public boolean containsKey(SshPublicKey key) { + return keys.containsValue(key); + } + + /** +* +* +* @param formatted +* @param serverId +* @param loader +* +* @return +* +* @throws RemoteIdentificationException +* @throws IOException +* @throws InvalidSshKeyException +*/ + public static AuthorizedKeys parse(byte[] formatted, String serverId, + String hostname, AuthorizedKeysFileLoader loader) + throws RemoteIdentificationException, IOException, + InvalidSshKeyException { + AuthorizedKeysFormat format = RemoteIdentificationFactory.getInstance(serverId, + hostname).getAuthorizedKeysFormat(); + + if (format.requiresKeyFiles()) { + return format.unformat(formatted, loader); + } else { + return format.unformat(formatted); + } + } + + /** +* +* +* @param keys +* @param serverId +* @param saver +* +* @return +* +* @throws RemoteIdentificationException +* @throws IOException +* @throws InvalidSshKeyException +*/ + public static byte[] create(AuthorizedKeys keys, String serverId, + String hostname, AuthorizedKeysFileSaver saver) + throws RemoteIdentificationException, IOException, + InvalidSshKeyException { + AuthorizedKeysFormat format = RemoteIdentificationFactory.getInstance(serverId, + hostname).getAuthorizedKeysFormat(); + + if (format.requiresKeyFiles()) { + return format.format(keys, saver); + } else { + return format.format(keys); + } + } +} diff --git a/src/com/sshtools/common/automate/AuthorizedKeysFileLoader.java b/src/com/sshtools/common/automate/AuthorizedKeysFileLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..e01eb44ce31aea0d0f794c836171996454ed71a8 --- /dev/null +++ b/src/com/sshtools/common/automate/AuthorizedKeysFileLoader.java @@ -0,0 +1,47 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import java.io.*; + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface AuthorizedKeysFileLoader { + /** + * + * + * @param filename + * + * @return + * + * @throws IOException + */ + public byte[] loadFile(String filename) throws IOException; +} diff --git a/src/com/sshtools/common/automate/AuthorizedKeysFileSaver.java b/src/com/sshtools/common/automate/AuthorizedKeysFileSaver.java new file mode 100644 index 0000000000000000000000000000000000000000..ce58576d435aec552f03aaa6e72782468737328a --- /dev/null +++ b/src/com/sshtools/common/automate/AuthorizedKeysFileSaver.java @@ -0,0 +1,48 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface AuthorizedKeysFileSaver { + /** +* +* +* @param filename +* @param filedata +* +* @throws IOException +*/ + public void saveFile(String filename, byte[] filedata) + throws IOException; +} diff --git a/src/com/sshtools/common/automate/AuthorizedKeysFormat.java b/src/com/sshtools/common/automate/AuthorizedKeysFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..206f02693149c5def8f5f50ef4035a4b3c946b07 --- /dev/null +++ b/src/com/sshtools/common/automate/AuthorizedKeysFormat.java @@ -0,0 +1,101 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface AuthorizedKeysFormat { + /** +* +* +* @param keys +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public byte[] format(AuthorizedKeys keys) + throws IOException, InvalidSshKeyException; + + /** +* +* +* @param keys +* @param saver +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public byte[] format(AuthorizedKeys keys, AuthorizedKeysFileSaver saver) + throws IOException, InvalidSshKeyException; + + /** +* +* +* @param formatted +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public AuthorizedKeys unformat(byte[] formatted) + throws IOException, InvalidSshKeyException; + + /** +* +* +* @param formatted +* @param loader +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public AuthorizedKeys unformat(byte[] formatted, + AuthorizedKeysFileLoader loader) + throws IOException, InvalidSshKeyException; + + /** +* +* +* @return +*/ + public boolean requiresKeyFiles(); +} diff --git a/src/com/sshtools/common/automate/AutomationConfiguration.java b/src/com/sshtools/common/automate/AutomationConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..594cc0b85a975c3793a80f8314dc1d75f24c2bf8 --- /dev/null +++ b/src/com/sshtools/common/automate/AutomationConfiguration.java @@ -0,0 +1,224 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.IOException; +import java.io.InputStream; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class AutomationConfiguration { + private HashMap remoteIdentifications = new HashMap(); + + /** +* Creates a new AutomationConfiguration object. +* +* @param in +* +* @throws IOException +* @throws SAXException +* @throws ParserConfigurationException +*/ + public AutomationConfiguration(InputStream in) + throws IOException, SAXException, ParserConfigurationException { + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + saxParser.parse(in, new AutomationConfigurationSAXHandler()); + } + + /** +* +* +* @return +*/ + public Map getRemoteIdentifications() { + return remoteIdentifications; + } + + /** +* +* +* @param args +*/ + public static void main(String[] args) { + try { + } catch (Exception e) { + e.printStackTrace(); + } + } + + private class AutomationConfigurationSAXHandler extends DefaultHandler { + private String AUTOMATION_ELEMENT = "Automation"; + private String REMOTEID_ELEMENT = "RemoteIdentification"; + private String AUTHORIZEDKEYSFORMAT_ELEMENT = "AuthorizedKeysFormat"; + private String RULE_ELEMENT = "Rule"; + private String STARTSWITH_ATTRIBUTE = "startsWith"; + private String CONTAINS_ATTRIBUTE = "contains"; + private String DEFAULTNAME_ATTRIBUTE = "defaultName"; + private String NAME_ATTRIBUTE = "name"; + private String IMPLEMENTATION_ATTRIBUTE = "implementationClass"; + private String PRIORITY_ATTRIBUTE = "priority"; + private String DEFAULTPATH_ATTRIBUTE = "defaultPath"; + private String currentElement = null; + private RemoteIdentification currentRID = null; + + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (!qname.equals(AUTOMATION_ELEMENT)) { + throw new SAXException("Unexpected root element <" + qname + + ">"); + } + } else { + if (currentElement.equals(AUTOMATION_ELEMENT)) { + if (qname.equals(REMOTEID_ELEMENT)) { + String defaultName = attrs.getValue(DEFAULTNAME_ATTRIBUTE); + + if (defaultName == null) { + throw new SAXException(DEFAULTNAME_ATTRIBUTE + + " attribute must be specified"); + } + + currentRID = new RemoteIdentification(defaultName); + } else { + throw new SAXException("Unexpected element <" + qname + + ">"); + } + } else if (currentElement.equals(REMOTEID_ELEMENT)) { + if (qname.equals(RULE_ELEMENT)) { + String startsWith = attrs.getValue(STARTSWITH_ATTRIBUTE); + String contains = attrs.getValue(CONTAINS_ATTRIBUTE); + String name = attrs.getValue(NAME_ATTRIBUTE); + String priority = attrs.getValue(PRIORITY_ATTRIBUTE); + + try { + RemoteIdentificationRule rule = new RemoteIdentificationRule(); + + if (startsWith != null) { + rule.addExpression(STARTSWITH_ATTRIBUTE, + startsWith); + } + + if (contains != null) { + rule.addExpression(CONTAINS_ATTRIBUTE, contains); + } + + if (name != null) { + rule.setName(name); + } + + try { + if (priority != null) { + rule.setPriority(Integer.parseInt(priority)); + } + } catch (NumberFormatException ex1) { + throw new SAXException( + "Failed to parse priority value! value=" + + priority); + } + + currentRID.addRule(rule); + } catch (UnsupportedRuleException ure) { + throw new SAXException(ure.getMessage()); + } + } else if (qname.equals(AUTHORIZEDKEYSFORMAT_ELEMENT)) { + String implementationClass = attrs.getValue(IMPLEMENTATION_ATTRIBUTE); + String defaultPath = attrs.getValue(DEFAULTPATH_ATTRIBUTE); + + if (implementationClass == null) { + throw new SAXException(IMPLEMENTATION_ATTRIBUTE + + " attribute is required"); + } + + try { + currentRID.setAuthorizedKeysFormat(Class.forName( + implementationClass)); + currentRID.setAuthorizedKeysDefaultPath(defaultPath); + } catch (ClassNotFoundException ex) { + throw new SAXException(ex.getMessage()); + } + } else { + throw new SAXException("Unexpected element <" + qname + + ">"); + } + } else { + throw new SAXException("Unexpected element <" + qname + + ">"); + } + } + + currentElement = qname; + } + + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement != null) { + if (!currentElement.equals(qname)) { + throw new SAXException("Unexpected end element found <" + + qname + ">"); + } + + if (currentElement.equals(REMOTEID_ELEMENT)) { + if (currentRID.getRules().size() > 0) { + remoteIdentifications.put(currentRID.getDefaultName(), + currentRID); + } else { + throw new SAXException("<" + REMOTEID_ELEMENT + "> " + + " requires at least one child <" + RULE_ELEMENT + + "> element!"); + } + + currentElement = AUTOMATION_ELEMENT; + } else if (currentElement.equals(RULE_ELEMENT)) { + currentElement = REMOTEID_ELEMENT; + } else if (currentElement.equals(AUTHORIZEDKEYSFORMAT_ELEMENT)) { + currentElement = REMOTEID_ELEMENT; + } else if (currentElement.equals(AUTOMATION_ELEMENT)) { + currentElement = null; + } else { + throw new SAXException("Unexpected end element <" + qname + + ">"); + } + } + } + } +} diff --git a/src/com/sshtools/common/automate/OpenSSHAuthorizedKeysFormat.java b/src/com/sshtools/common/automate/OpenSSHAuthorizedKeysFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..19997f78f15521ad77a6d3ebfa2fecbcc72505b9 --- /dev/null +++ b/src/com/sshtools/common/automate/OpenSSHAuthorizedKeysFormat.java @@ -0,0 +1,151 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.OpenSSHPublicKeyFormat; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKeyFile; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class OpenSSHAuthorizedKeysFormat implements AuthorizedKeysFormat { + /** +* +* +* @param keys +* @param saver +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +* @throws UnsupportedOperationException +*/ + public byte[] format(AuthorizedKeys keys, AuthorizedKeysFileSaver saver) + throws IOException, InvalidSshKeyException { + throw new UnsupportedOperationException( + "The OpenSSH authorized key file does not support additional key files!"); + } + + /** +* +* +* @param formatted +* @param loader +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +* @throws UnsupportedOperationException +*/ + public AuthorizedKeys unformat(byte[] formatted, + AuthorizedKeysFileLoader loader) + throws IOException, InvalidSshKeyException { + throw new UnsupportedOperationException( + "The OpenSSH authorized key file does not support additional key files!"); + } + + /** +* +* +* @param keys +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public byte[] format(AuthorizedKeys keys) + throws IOException, InvalidSshKeyException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + SshPublicKeyFile pubfile; + OpenSSHPublicKeyFormat openssh = new OpenSSHPublicKeyFormat(); + Map.Entry entry; + + for (Iterator it = keys.getAuthorizedKeys().entrySet().iterator(); + (it != null) && it.hasNext();) { + entry = (Map.Entry) it.next(); + openssh.setComment((String) entry.getValue()); + pubfile = SshPublicKeyFile.create((SshPublicKey) entry.getKey(), + openssh); + out.write(pubfile.toString().getBytes("US-ASCII")); + out.write('\n'); + } + + return out.toByteArray(); + } + + /** +* +* +* @param formatted +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public AuthorizedKeys unformat(byte[] formatted) + throws IOException, InvalidSshKeyException { + AuthorizedKeys keys = new AuthorizedKeys(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + new ByteArrayInputStream(formatted))); + String line; + SshPublicKeyFile pubfile; + + while ((line = reader.readLine()) != null) { + pubfile = SshPublicKeyFile.parse(line.getBytes("US-ASCII")); + keys.addKey(pubfile.getComment(), pubfile.toPublicKey()); + } + + return keys; + } + + /** +* +* +* @return +*/ + public boolean requiresKeyFiles() { + return false; + } +} diff --git a/src/com/sshtools/common/automate/RemoteIdentification.java b/src/com/sshtools/common/automate/RemoteIdentification.java new file mode 100644 index 0000000000000000000000000000000000000000..0f3e1a8ce5631ee3bfe23a40fe47d02fcb9e2d93 --- /dev/null +++ b/src/com/sshtools/common/automate/RemoteIdentification.java @@ -0,0 +1,400 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.j2ssh.SftpClient; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class RemoteIdentification { + /** */ + public static final int ADD_AUTHORIZEDKEY = 1; + + /** */ + public static final int REMOVE_AUTHORIZEDKEY = 2; + private String defaultName; + private Vector rules = new Vector(); + private Class authorizedKeysFormat; + private String defaultPath; + + /** */ + protected Log log = LogFactory.getLog(RemoteIdentification.class); + + /** +* Creates a new RemoteIdentification object. +* +* @param defaultName +*/ + public RemoteIdentification(String defaultName) { + this.defaultName = defaultName; + } + + /** +* +* +* @return +*/ + protected List getRules() { + return rules; + } + + /** +* +* +* @return +*/ + public String getDefaultName() { + return defaultName; + } + + /** +* +* +* @param ident +* +* @return +* +* @throws RemoteIdentificationException +*/ + public String getName(String ident) throws RemoteIdentificationException { + boolean pass = false; + Iterator it = rules.iterator(); + Vector passed = new Vector(); + RemoteIdentificationRule rule; + String rulename = null; + + // Check all the rules + while (it.hasNext()) { + rule = (RemoteIdentificationRule) it.next(); + + if (rule.testRule(ident)) { + passed.add(rule); + } + } + + if (passed.size() > 0) { + // Select the highest priority rule where 0=highest 10=lowest + it = passed.iterator(); + + RemoteIdentificationRule ret = null; + + while (it.hasNext()) { + rule = (RemoteIdentificationRule) it.next(); + + if (ret == null) { + ret = rule; + } else { + if (rule.getPriority() < ret.getPriority()) { + ret = rule; + } + } + } + + if (ret.getName() != null) { + return ret.getName(); + } else { + return defaultName; + } + } else { + throw new RemoteIdentificationException( + "No rules exist to identify the remote host with ident string " + + ident); + } + } + + /** +* +* +* @param rule +*/ + protected void addRule(RemoteIdentificationRule rule) { + rules.add(rule); + } + + /** +* +* +* @param ident +* +* @return +*/ + protected boolean testRules(String ident) { + boolean pass = false; + Iterator it = rules.iterator(); + RemoteIdentificationRule rule; + + while (it.hasNext() && !pass) { + rule = (RemoteIdentificationRule) it.next(); + pass = rule.testRule(ident); + } + + return pass; + } + + /** +* +* +* @param implementationClass +*/ + protected void setAuthorizedKeysFormat(Class implementationClass) { + authorizedKeysFormat = implementationClass; + } + + /** +* +* +* @param defaultPath +*/ + protected void setAuthorizedKeysDefaultPath(String defaultPath) { + this.defaultPath = defaultPath; + } + + /** +* +* +* @return +*/ + public String getAuthorizedKeysDefaultPath() { + return defaultPath; + } + + /** +* +* +* @return +* +* @throws RemoteIdentificationException +*/ + public AuthorizedKeysFormat getAuthorizedKeysFormat() + throws RemoteIdentificationException { + try { + if (authorizedKeysFormat != null) { + return (AuthorizedKeysFormat) authorizedKeysFormat.newInstance(); + } else { + throw new RemoteIdentificationException( + "There is no authorized keys format set for this remote id"); + } + } catch (Exception ex) { + throw new RemoteIdentificationException("Failed to instansiate " + + authorizedKeysFormat.getName()); + } + } + + /** +* +* +* @param sftp +* @param serverId +* @param system +* @param username +* @param pk +* @param authorizationFile +* @param mode +* +* @return +* +* @throws RemoteIdentificationException +*/ + public boolean configureUserAccess(SftpClient sftp, String serverId, + String system, String username, SshPublicKey pk, + String authorizationFile, int mode) + throws RemoteIdentificationException { + Vector keys = new Vector(); + keys.add(pk); + + return configureUserAccess(sftp, serverId, system, username, keys, + authorizationFile, mode); + } + + /** +* +* +* @param sftp +* @param serverId +* @param system +* @param username +* @param keys +* @param authorizationFile +* @param mode +* +* @return +* +* @throws RemoteIdentificationException +*/ + public boolean configureUserAccess(final SftpClient sftp, + final String serverId, String system, String username, List keys, + String authorizationFile, int mode) + throws RemoteIdentificationException { + try { + if (sftp.isClosed()) { + throw new RemoteIdentificationException( + "SFTP connection must be open"); + } + + if (authorizationFile == null) { + throw new RemoteIdentificationException( + "authorization file cannot be null"); + } + + if ((mode != ADD_AUTHORIZEDKEY) && (mode != REMOVE_AUTHORIZEDKEY)) { + throw new RemoteIdentificationException( + "Invalid configuration mode specifed in call to configureUserAccess"); + } + + AuthorizedKeys authorizedKeys; + authorizationFile.replace('\\', '/'); + + final String directory = ((authorizationFile.lastIndexOf("/") > 0) + ? authorizationFile.substring(0, + authorizationFile.lastIndexOf("/") + 1) : ""); + + try { + // Remove the old backup - ignore the error + try { + sftp.rm(authorizationFile + ".bak"); + } catch (IOException ex) { + } + + // Change the current authorization file to the backup + sftp.rename(authorizationFile, authorizationFile + ".bak"); + log.info("Opening existing authorized keys file from " + + authorizationFile + ".bak"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + sftp.get(authorizationFile + ".bak", out); + + byte[] backup = out.toByteArray(); + out.close(); + + // Obtain the current authoized keys settings + log.info("Parsing authorized keys file"); + authorizedKeys = AuthorizedKeys.parse(backup, serverId, system, + new AuthorizedKeysFileLoader() { + public byte[] loadFile(String filename) + throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + sftp.get(directory + filename, out); + out.close(); + + return out.toByteArray(); + } + }); + } catch (IOException ioe) { + // Could not open so create a new file + authorizedKeys = new AuthorizedKeys(); + } catch (RemoteIdentificationException rie) { + throw new RemoteIdentificationException( + "Open3SP cannot identify the remote host.\n" + + "Please email support@open3sp.org with specifying 'remote identification' in the subject and supplying the server type and the follwing data '" + + serverId + "'"); + } + + log.info("Updating authorized keys file"); + + // Check the existing keys and add any that are not present + SshPublicKey pk; + + for (Iterator x = keys.iterator(); x.hasNext();) { + pk = (SshPublicKey) x.next(); + + if (!authorizedKeys.containsKey(pk) && + (mode == ADD_AUTHORIZEDKEY)) { + authorizedKeys.addKey(username, pk); + } else if (authorizedKeys.containsKey(pk) && + (mode == REMOVE_AUTHORIZEDKEY)) { + authorizedKeys.removeKey(pk); + } + } + + // Verfiy that the directory exists? + log.info("Verifying directory " + directory); + + int umask = sftp.umask(0022); + sftp.mkdirs(directory); + + // Output the new file + log.info("Writing new authorized keys file to " + + authorizationFile); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Output the authorization file to a ByteArrayOutputStream + out.write(AuthorizedKeys.create(authorizedKeys, serverId, system, + new AuthorizedKeysFileSaver() { + public void saveFile(String filename, byte[] filedata) + throws IOException { + //SftpFile file = null; + ByteArrayInputStream in = null; + + try { + in = new ByteArrayInputStream(filedata); + sftp.put(in, directory + filename); + } catch (IOException ex) { + log.info("Error writing public key file to server" + + filename, ex); + } finally { + if (in != null) { + in.close(); + } + } + } + })); + out.close(); + + // Copy the new authorisation file to the server + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + sftp.umask(0133); + sftp.put(in, authorizationFile); + sftp.umask(umask); + + return true; + } catch (IOException ioe) { + throw new RemoteIdentificationException(ioe.getMessage()); + } catch (RemoteIdentificationException rie) { + throw new RemoteIdentificationException( + "SSHTools cannot identify the remote host.\n" + + "Please email support@sshtools.com specifying 'remote identification' in the subject, supplying the server type and the following data: '" + + serverId + "'"); + } + } +} diff --git a/src/com/sshtools/common/automate/RemoteIdentificationException.java b/src/com/sshtools/common/automate/RemoteIdentificationException.java new file mode 100644 index 0000000000000000000000000000000000000000..ab599054b4edba7c92f6f0061bd3fa1beca66c3a --- /dev/null +++ b/src/com/sshtools/common/automate/RemoteIdentificationException.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class RemoteIdentificationException extends Exception { + /** +* Creates a new RemoteIdentificationException object. +* +* @param msg +*/ + public RemoteIdentificationException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/common/automate/RemoteIdentificationFactory.java b/src/com/sshtools/common/automate/RemoteIdentificationFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ae02fa4ca2b70dc5ed8de8d369d30bd59ca73ab5 --- /dev/null +++ b/src/com/sshtools/common/automate/RemoteIdentificationFactory.java @@ -0,0 +1,122 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class RemoteIdentificationFactory { + private static Map remoteIdentifications = null; + + static { + try { + if (ConfigurationLoader.isConfigurationAvailable( + AutomationConfiguration.class)) { + remoteIdentifications = ((AutomationConfiguration) ConfigurationLoader.getConfiguration(AutomationConfiguration.class)).getRemoteIdentifications(); + } + } catch (ConfigurationException ex) { + } + } + + /** +* +* +* @param serverId +* +* @return +* +* @throws RemoteIdentificationException +*/ + public static synchronized RemoteIdentification getInstance( + String serverId, String hostname) throws RemoteIdentificationException { + if (remoteIdentifications == null) { + throw new RemoteIdentificationException( + "There are no remote identification rules!"); + } + + Iterator it = remoteIdentifications.entrySet().iterator(); + Map.Entry entry; + RemoteIdentification rid; + + // Check the hostname first + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + rid = (RemoteIdentification) entry.getValue(); + + if (hostname != null) { + if (rid.getDefaultName().equals(hostname)) { + return rid; + } + } + } + + it = remoteIdentifications.entrySet().iterator(); + + // Now check against the rules + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + rid = (RemoteIdentification) entry.getValue(); + + if (rid.testRules(serverId)) { + return rid; + } + } + + throw new RemoteIdentificationException( + "Failed to find a remote identification rule"); + } + + /** +* +* +* @param args +*/ + public static void main(String[] args) { + try { + RemoteIdentification rid; + String serverId = "http://www.sshtools.com J2SSH 0.1.1 beta [CLIENT]"; + rid = getInstance(serverId, null); + System.out.println("Remote Identification: " + + rid.getName(serverId)); + serverId = "OpenSSH_3.4p1"; + rid = getInstance(serverId, null); + System.out.println("Remote Identification: " + + rid.getName(serverId)); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/com/sshtools/common/automate/RemoteIdentificationRule.java b/src/com/sshtools/common/automate/RemoteIdentificationRule.java new file mode 100644 index 0000000000000000000000000000000000000000..542053a31646065b7fc5cd9ea019a5955a92e73b --- /dev/null +++ b/src/com/sshtools/common/automate/RemoteIdentificationRule.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class RemoteIdentificationRule { + private static HashSet allowedOperations = new HashSet(); + + static { + allowedOperations.add("startsWith"); + allowedOperations.add("contains"); + } + + private HashMap expressions = new HashMap(); + private int priority = 10; + private String name; + + /** +* +* +* @param identification +* +* @return +*/ + public boolean testRule(String identification) { + // Get the software version portion of the id string + String svc = identification.substring(identification.lastIndexOf("-") + + 1); + Iterator it = expressions.entrySet().iterator(); + Map.Entry entry; + boolean pass = false; + String operation; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + operation = (String) entry.getKey(); + + if (operation.equals("startsWith")) { + pass = svc.startsWith((String) entry.getValue()); + } + + if (operation.equals("contains")) { + pass = (svc.indexOf((String) entry.getValue()) >= 0); + } + } + + return pass; + } + + /** +* +* +* @param priority +*/ + protected void setPriority(int priority) { + this.priority = priority; + } + + /** +* +* +* @param operation +* @param value +* +* @throws UnsupportedRuleException +*/ + protected void addExpression(String operation, String value) + throws UnsupportedRuleException { + if (allowedOperations.contains(operation)) { + expressions.put(operation, value); + } else { + throw new UnsupportedRuleException("The rule '" + operation + + "' is not supported"); + } + } + + /** +* +* +* @param name +*/ + protected void setName(String name) { + this.name = name; + } + + /** +* +* +* @return +*/ + public String getName() { + return name; + } + + /** +* +* +* @return +*/ + public int getPriority() { + return priority; + } +} diff --git a/src/com/sshtools/common/automate/SSH2AuthorizedKeysFormat.java b/src/com/sshtools/common/automate/SSH2AuthorizedKeysFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..30d0047865e12ebb35d3998f52694c418bc9e97a --- /dev/null +++ b/src/com/sshtools/common/automate/SSH2AuthorizedKeysFormat.java @@ -0,0 +1,174 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SECSHPublicKeyFormat; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKeyFile; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class SSH2AuthorizedKeysFormat implements AuthorizedKeysFormat { + private static final String header = "# Open3SP auto-generated authorization file\n"; + private static final String key = "key "; + + /** +* +* +* @param keys +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +* @throws java.lang.UnsupportedOperationException +*/ + public byte[] format(AuthorizedKeys keys) + throws IOException, InvalidSshKeyException { + throw new java.lang.UnsupportedOperationException( + "SSH2 authorized keys format requries seperate key files!"); + } + + /** +* +* +* @param formatted +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +* @throws java.lang.UnsupportedOperationException +*/ + public AuthorizedKeys unformat(byte[] formatted) + throws IOException, InvalidSshKeyException { + throw new java.lang.UnsupportedOperationException( + "SSH2 authorized keys format requries seperate key files!"); + } + + /** +* +* +* @param keys +* @param saver +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public byte[] format(AuthorizedKeys keys, AuthorizedKeysFileSaver saver) + throws IOException, InvalidSshKeyException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(header.getBytes("US-ASCII")); + + SshPublicKeyFile pubfile; + SECSHPublicKeyFormat secsh = new SECSHPublicKeyFormat(); + Map.Entry entry; + + for (Iterator it = keys.getAuthorizedKeys().entrySet().iterator(); + (it != null) && it.hasNext();) { + entry = (Map.Entry) it.next(); + + // Write out the public key file + String username = (String) entry.getValue(); + String filename = username + ".pub"; + secsh.setComment(username); + pubfile = SshPublicKeyFile.create((SshPublicKey) entry.getKey(), + secsh); + saver.saveFile(filename, pubfile.toString().getBytes("US-ASCII")); + + // Write out the key entry + out.write(key.getBytes("US-ASCII")); + out.write(filename.getBytes("US-ASCII")); + out.write('\n'); + } + + return out.toByteArray(); + } + + /** +* +* +* @param formatted +* @param loader +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public AuthorizedKeys unformat(byte[] formatted, + AuthorizedKeysFileLoader loader) + throws IOException, InvalidSshKeyException { + AuthorizedKeys keys = new AuthorizedKeys(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + new ByteArrayInputStream(formatted))); + String line; + SshPublicKeyFile pubfile; + String filename; + String username; + + while ((line = reader.readLine()) != null) { + if (line.trim().startsWith("key")) { + // Get the filename and load + filename = line.substring(line.trim().lastIndexOf(" ") + 1) + .trim(); + pubfile = SshPublicKeyFile.parse(loader.loadFile(filename)); + + // Get the username from the filename - .pub + username = filename.substring(0, filename.length() - 4); + keys.addKey(username, pubfile.toPublicKey()); + } + } + + return keys; + } + + /** +* +* +* @return +*/ + public boolean requiresKeyFiles() { + return true; + } +} diff --git a/src/com/sshtools/common/automate/SshtoolsAuthorizedKeysFormat.java b/src/com/sshtools/common/automate/SshtoolsAuthorizedKeysFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..88e93d06f0d075b0f00e3eef019e858360134347 --- /dev/null +++ b/src/com/sshtools/common/automate/SshtoolsAuthorizedKeysFormat.java @@ -0,0 +1,168 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.common.configuration.Authorization; + +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SECSHPublicKeyFormat; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKeyFile; + +import org.xml.sax.SAXException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshtoolsAuthorizedKeysFormat implements AuthorizedKeysFormat { + /** +* +* +* @param keys +* +* @return +* +* @throws java.lang.UnsupportedOperationException +*/ + public byte[] format(AuthorizedKeys keys) { + throw new java.lang.UnsupportedOperationException( + "SSHTools authorized keys format requries seperate key files!"); + } + + /** +* +* +* @param formatted +* +* @return +* +* @throws java.lang.UnsupportedOperationException +*/ + public AuthorizedKeys unformat(byte[] formatted) { + throw new java.lang.UnsupportedOperationException( + "SSHTools authorized keys format requries seperate key files!"); + } + + /** +* +* +* @param keys +* @param saver +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public byte[] format(AuthorizedKeys keys, AuthorizedKeysFileSaver saver) + throws IOException, InvalidSshKeyException { + Authorization authorization = new Authorization(); + SshPublicKeyFile pubfile; + SECSHPublicKeyFormat secsh = new SECSHPublicKeyFormat(); + Map.Entry entry; + + for (Iterator it = keys.getAuthorizedKeys().entrySet().iterator(); + (it != null) && it.hasNext();) { + entry = (Map.Entry) it.next(); + + // Write out the public key file + String username = (String) entry.getValue(); + String filename = username + ".pub"; + secsh.setComment(username); + pubfile = SshPublicKeyFile.create((SshPublicKey) entry.getKey(), + secsh); + saver.saveFile(filename, pubfile.toString().getBytes("US-ASCII")); + + // Write out the key entry + authorization.addKey(filename); + } + + return authorization.toString().getBytes("US-ASCII"); + } + + /** +* +* +* @param formatted +* @param loader +* +* @return +* +* @throws IOException +* @throws InvalidSshKeyException +*/ + public AuthorizedKeys unformat(byte[] formatted, + AuthorizedKeysFileLoader loader) + throws IOException, InvalidSshKeyException { + try { + AuthorizedKeys keys = new AuthorizedKeys(); + Authorization authorization = new Authorization(new ByteArrayInputStream( + formatted)); + List keyfiles = authorization.getAuthorizedKeys(); + Iterator it = keyfiles.iterator(); + String filename; + SshPublicKeyFile pubfile; + String username; + + while (it.hasNext()) { + filename = (String) it.next(); + pubfile = SshPublicKeyFile.parse(loader.loadFile(filename)); + username = filename.substring(0, filename.length() - 4); + keys.addKey(username, pubfile.toPublicKey()); + } + + return keys; + } catch (ParserConfigurationException ex) { + throw new IOException("Failed to read authorization file: " + + ex.getMessage()); + } catch (SAXException ex) { + throw new IOException("Failed to read authorization file: " + + ex.getMessage()); + } + } + + /** +* +* +* @return +*/ + public boolean requiresKeyFiles() { + return true; + } +} diff --git a/src/com/sshtools/common/automate/SystemVerification.java b/src/com/sshtools/common/automate/SystemVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..dda8feca3140ca3edccdb562a0eba967cec6e8d2 --- /dev/null +++ b/src/com/sshtools/common/automate/SystemVerification.java @@ -0,0 +1,73 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + +import com.sshtools.j2ssh.transport.HostKeyVerification; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public interface SystemVerification extends HostKeyVerification { + /** +* +* +* @param defaultPath +* +* @return +* +* @throws RemoteIdentificationException +*/ + public String inputAuthorizationFile(String defaultPath) + throws RemoteIdentificationException; + + /** +* +* +* @param prompt +* +* @return +* +* @throws RemoteIdentificationException +*/ + public String inputUsername(String prompt) + throws RemoteIdentificationException; + + /** +* +* +* @param prompt +* +* @return +* +* @throws RemoteIdentificationException +*/ + public String inputPassword(String prompt) + throws RemoteIdentificationException; +} diff --git a/src/com/sshtools/common/automate/UnsupportedRuleException.java b/src/com/sshtools/common/automate/UnsupportedRuleException.java new file mode 100644 index 0000000000000000000000000000000000000000..b916fb24a9900e8ae2e73fcf5137bde5d4465961 --- /dev/null +++ b/src/com/sshtools/common/automate/UnsupportedRuleException.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.automate; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class UnsupportedRuleException extends Exception { + /** +* Creates a new UnsupportedRuleException object. +* +* @param msg +*/ + public UnsupportedRuleException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/common/configuration/Authorization.java b/src/com/sshtools/common/configuration/Authorization.java new file mode 100644 index 0000000000000000000000000000000000000000..600407773ff92523bfebb24ca84bbddf2565b2b3 --- /dev/null +++ b/src/com/sshtools/common/configuration/Authorization.java @@ -0,0 +1,164 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.configuration; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import java.io.*; + +import java.util.*; + +import javax.xml.parsers.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class Authorization extends DefaultHandler { + private static final String AUTHORIZEDKEYS_ELEMENT = "AuthorizedKeys"; + private static final String KEY_ELEMENT = "Key"; + private ArrayList authorizedKeys = new ArrayList(); + + /** +* Creates a new Authorization object. +* +* @param in +* +* @throws SAXException +* @throws ParserConfigurationException +* @throws IOException +*/ + public Authorization(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + authorizedKeys.clear(); + saxParser.parse(in, new AuthorizedKeysSAXHandler()); + } + + /** +* Creates a new Authorization object. +*/ + public Authorization() { + // Creates an empty authorization file + } + + /** +* +* +* @return +*/ + public List getAuthorizedKeys() { + return (List) authorizedKeys.clone(); + } + + /** +* +* +* @param keyfile +*/ + public void addKey(String keyfile) { + authorizedKeys.add(keyfile); + } + + /** +* +* +* @return +*/ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += "<!-- SSHTools Public Key Authorization File -->\n"; + xml += ("<" + AUTHORIZEDKEYS_ELEMENT + ">\n"); + xml += "<!-- Enter authorized public key elements here -->\n"; + + Iterator it = authorizedKeys.iterator(); + + while (it.hasNext()) { + xml += (" <" + KEY_ELEMENT + ">" + it.next().toString() + "</" + + KEY_ELEMENT + ">\n"); + } + + xml += ("</" + AUTHORIZEDKEYS_ELEMENT + ">"); + + return xml; + } + + class AuthorizedKeysSAXHandler extends DefaultHandler { + private String currentElement = null; + + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (!qname.equals(AUTHORIZEDKEYS_ELEMENT)) { + throw new SAXException("Unexpected root element " + qname); + } + } else { + if (currentElement.equals(AUTHORIZEDKEYS_ELEMENT)) { + if (!qname.equals(KEY_ELEMENT)) { + throw new SAXException("Unexpected element " + qname); + } + } else { + throw new SAXException("Unexpected element " + qname); + } + } + + currentElement = qname; + } + + public void characters(char[] ch, int start, int length) + throws SAXException { + if (currentElement != null) { + if (currentElement.equals(KEY_ELEMENT)) { + String key = new String(ch, start, length); + authorizedKeys.add(key); + } + } + } + + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement != null) { + if (!currentElement.equals(qname)) { + throw new SAXException("Unexpected end element found " + + qname); + } + + if (currentElement.equals(KEY_ELEMENT)) { + currentElement = AUTHORIZEDKEYS_ELEMENT; + } else if (currentElement.equals(AUTHORIZEDKEYS_ELEMENT)) { + currentElement = null; + } else { + throw new SAXException("Unexpected end element " + qname); + } + } + } + } +} diff --git a/src/com/sshtools/common/configuration/InvalidProfileFileException.java b/src/com/sshtools/common/configuration/InvalidProfileFileException.java new file mode 100644 index 0000000000000000000000000000000000000000..7742ff406c092c12a6b4c15b5d2a475f4f66b113 --- /dev/null +++ b/src/com/sshtools/common/configuration/InvalidProfileFileException.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.configuration; + +import com.sshtools.j2ssh.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class InvalidProfileFileException extends SshException { + /** +* Creates a new InvalidProfileFileException object. +* +* @param msg +*/ + public InvalidProfileFileException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/common/configuration/SshAPIConfiguration.java b/src/com/sshtools/common/configuration/SshAPIConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..3f1c2819a57526014ab5d5d6ecf550af792bd9ac --- /dev/null +++ b/src/com/sshtools/common/configuration/SshAPIConfiguration.java @@ -0,0 +1,628 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.configuration; + +import com.sshtools.j2ssh.configuration.*; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import java.io.*; + +import java.util.*; + +import javax.xml.parsers.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class SshAPIConfiguration extends DefaultHandler + implements com.sshtools.j2ssh.configuration.SshAPIConfiguration { + private String defaultCipher = null; + private String defaultMac = null; + private String defaultCompression = null; + private String defaultPublicKey = null; + private String defaultKeyExchange = null; + private List cipherExtensions = new ArrayList(); + private List macExtensions = new ArrayList(); + private List compressionExtensions = new ArrayList(); + private List pkExtensions = new ArrayList(); + private List kexExtensions = new ArrayList(); + private List authExtensions = new ArrayList(); + private List pkFormats = new ArrayList(); + private List prvFormats = new ArrayList(); + private String defaultPublicFormat = null; + private String defaultPrivateFormat = null; + private String currentElement = null; + private String parentElement = null; + private List currentList = null; + private ExtensionAlgorithm currentExt = null; + + /** +* Creates a new SshAPIConfiguration object. +* +* @param in +* +* @throws SAXException +* @throws ParserConfigurationException +* @throws IOException +*/ + public SshAPIConfiguration(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + reload(in); + } + + /** +* +* +* @param in +* +* @throws SAXException +* @throws ParserConfigurationException +* @throws IOException +*/ + public void reload(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + defaultCipher = null; + defaultMac = null; + defaultCompression = null; + defaultKeyExchange = null; + defaultPublicKey = null; + defaultPublicFormat = null; + defaultPrivateFormat = null; + cipherExtensions.clear(); + macExtensions.clear(); + compressionExtensions.clear(); + pkExtensions.clear(); + kexExtensions.clear(); + authExtensions.clear(); + pkFormats.clear(); + prvFormats.clear(); + + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + saxParser.parse(in, this); + } + + /** +* +* +* @param ch +* @param start +* @param length +* +* @throws SAXException +*/ + public void characters(char[] ch, int start, int length) + throws SAXException { + String value = new String(ch, start, length); + + if (currentElement != null) { + if (currentElement.equals("AlgorithmName")) { + if (currentExt != null) { + currentExt.setAlgorithmName(value); + } else { + throw new SAXException("Unexpected AlgorithmName element!"); + } + + return; + } + + if (currentElement.equals("ImplementationClass")) { + if (currentExt != null) { + currentExt.setImplementationClass(value); + } else { + throw new SAXException( + "Unexpected ImplementationClass element!"); + } + + return; + } + + if (currentElement.equals("DefaultAlgorithm")) { + if (parentElement.equals("CipherConfiguration")) { + defaultCipher = value; + } else if (parentElement.equals("MacConfiguration")) { + defaultMac = value; + } else if (parentElement.equals("CompressionConfiguration")) { + defaultCompression = value; + } else if (parentElement.equals("PublicKeyConfiguration")) { + defaultPublicKey = value; + } else if (parentElement.equals("KeyExchangeConfiguration")) { + defaultKeyExchange = value; + } else { + throw new SAXException( + "Unexpected parent elemenet for DefaultAlgorithm element"); + } + } + + if (currentElement.equals("DefaultPublicFormat")) { + defaultPublicFormat = value; + } + + if (currentElement.equals("DefaultPrivateFormat")) { + defaultPrivateFormat = value; + } + } + } + + /** +* +* +* @param uri +* @param localName +* @param qname +* +* @throws SAXException +*/ + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement != null) { + if (!currentElement.equals(qname)) { + throw new SAXException("Unexpected end element found " + qname); + } else if (currentElement.equals("SshAPIConfiguration")) { + currentElement = null; + } else if (currentElement.equals("CipherConfiguration") || + currentElement.equals("MacConfiguration") || + currentElement.equals("PublicKeyConfiguration") || + currentElement.equals("CompressionConfiguration") || + currentElement.equals("KeyExchangeConfiguration") || + currentElement.equals("AuthenticationConfiguration")) { + currentList = null; + currentElement = "SshAPIConfiguration"; + } else if (currentElement.equals("ExtensionAlgorithm")) { + if (currentExt == null) { + throw new SAXException( + "Critical error, null extension algortihm"); + } + + if ((currentExt.getAlgorithmName() == null) || + (currentExt.getImplementationClass() == null)) { + throw new SAXException( + "Unexpected end of ExtensionAlgorithm Element"); + } + + currentList.add(currentExt); + currentExt = null; + currentElement = parentElement; + } else if (currentElement.equals("DefaultAlgorithm") || + currentElement.equals("DefaultPublicFormat") || + currentElement.equals("DefaultPrivateFormat") || + currentElement.equals("PublicKeyFormat") || + currentElement.equals("PrivateKeyFormat")) { + currentElement = parentElement; + } else if (currentElement.equals("AlgorithmName")) { + currentElement = "ExtensionAlgorithm"; + } else if (currentElement.equals("ImplementationClass")) { + currentElement = "ExtensionAlgorithm"; + } else { + throw new SAXException("Unexpected end element " + qname); + } + } + } + + /** +* +* +* @param uri +* @param localName +* @param qname +* @param attrs +* +* @throws SAXException +*/ + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (!qname.equals("SshAPIConfiguration")) { + throw new SAXException("Unexpected root element " + qname); + } + } else { + if (currentElement.equals("SshAPIConfiguration")) { + if (!qname.equals("CipherConfiguration") && + !qname.equals("MacConfiguration") && + !qname.equals("CompressionConfiguration") && + !qname.equals("PublicKeyConfiguration") && + !qname.equals("AuthenticationConfiguration") && + !qname.equals("KeyExchangeConfiguration")) { + throw new SAXException("Unexpected <" + qname + + "> element after SshAPIConfiguration"); + } + } else if (currentElement.equals("CipherConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = cipherExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after CipherConfiguration"); + } + } else if (currentElement.equals("MacConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = macExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after CipherConfiguration"); + } + } else if (currentElement.equals("CompressionConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = compressionExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after CompressionConfiguration"); + } + } else if (currentElement.equals("PublicKeyConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = pkExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else if (qname.equals("PublicKeyFormat")) { + String cls = attrs.getValue("ImplementationClass"); + + if (cls == null) { + throw new SAXException( + "<PublicKeyFormat> element requries the ImplementationClass attribute"); + } + + pkFormats.add(cls); + } else if (qname.equals("PrivateKeyFormat")) { + String cls = attrs.getValue("ImplementationClass"); + + if (cls == null) { + throw new SAXException( + "<PrivateKeyFormat> element requries the ImplementationClass attribute"); + } + + prvFormats.add(cls); + } else if (qname.equals("DefaultPublicFormat")) { + parentElement = currentElement; + } else if (qname.equals("DefaultPrivateFormat")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after PublicKeyConfiguration"); + } + } else if (currentElement.equals("AuthenticationConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = authExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after AuthenticationConfiguration"); + } + } else if (currentElement.equals("KeyExchangeConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = kexExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after KeyExchangeConfiguration"); + } + } else if ((currentElement.equals("ExtensionAlgorithm") && + qname.equals("AlgorithmName")) || + (currentElement.equals("ExtensionAlgorithm") && + qname.equals("ImplementationClass"))) { + } else { + throw new SAXException("Unexpected element " + qname); + } + } + + currentElement = qname; + } + + /** +* +* +* @return +*/ + public List getCompressionExtensions() { + return compressionExtensions; + } + + /** +* +* +* @return +*/ + public List getCipherExtensions() { + return cipherExtensions; + } + + /** +* +* +* @return +*/ + public List getMacExtensions() { + return macExtensions; + } + + /** +* +* +* @return +*/ + public List getAuthenticationExtensions() { + return authExtensions; + } + + /** +* +* +* @return +*/ + public List getPublicKeyExtensions() { + return pkExtensions; + } + + /** +* +* +* @return +*/ + public List getKeyExchangeExtensions() { + return kexExtensions; + } + + /** +* +* +* @return +*/ + public String getDefaultCipher() { + return defaultCipher; + } + + /** +* +* +* @return +*/ + public String getDefaultMac() { + return defaultMac; + } + + /** +* +* +* @return +*/ + public String getDefaultCompression() { + return defaultCompression; + } + + /** +* +* +* @return +*/ + public String getDefaultPublicKey() { + return defaultPublicKey; + } + + /** +* +* +* @return +*/ + public String getDefaultKeyExchange() { + return defaultKeyExchange; + } + + /** +* +* +* @return +*/ + public String getDefaultPublicKeyFormat() { + return defaultPublicFormat; + } + + /** +* +* +* @return +*/ + public String getDefaultPrivateKeyFormat() { + return defaultPrivateFormat; + } + + /** +* +* +* @return +*/ + public List getPublicKeyFormats() { + return pkFormats; + } + + /** +* +* +* @return +*/ + public List getPrivateKeyFormats() { + return prvFormats; + } + + /** +* +* +* @return +*/ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += "<!-- Sshtools J2SSH Configuration file -->\n"; + xml += "<SshAPIConfiguration>\n"; + xml += " <!-- The Cipher configuration, add or overide default cipher implementations -->\n"; + xml += " <CipherConfiguration>\n"; + + Iterator it = cipherExtensions.iterator(); + ExtensionAlgorithm ext; + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultCipher + + "</DefaultAlgorithm>\n"); + xml += " </CipherConfiguration>\n"; + xml += " <!-- The Mac configuration, add or overide default mac implementations -->\n"; + xml += " <MacConfiguration>\n"; + it = macExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultMac + + "</DefaultAlgorithm>\n"); + xml += " </MacConfiguration>\n"; + xml += " <!-- The Compression configuration, add or overide default compression implementations -->\n"; + xml += " <CompressionConfiguration>\n"; + it = compressionExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultCompression + + "</DefaultAlgorithm>\n"); + xml += " </CompressionConfiguration>\n"; + xml += " <!-- The Public Key configuration, add or overide default public key implementations -->\n"; + xml += " <PublicKeyConfiguration>\n"; + it = pkExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultPublicKey + + "</DefaultAlgorithm>\n"); + it = pkFormats.iterator(); + + String cls; + + while (it.hasNext()) { + cls = (String) it.next(); + xml += (" <PublicKeyFormat ImplementationClass=\"" + cls + + "\"/>\n"); + } + + it = prvFormats.iterator(); + + while (it.hasNext()) { + cls = (String) it.next(); + xml += (" <PrivateKeyFormat ImplementationClass=\"" + cls + + "\"/>\n"); + } + + xml += (" <DefaultPublicFormat>" + defaultPublicFormat + + "</DefaultPublicFormat>\n"); + xml += (" <DefaultPrivateFormat>" + defaultPrivateFormat + + "</DefaultPrivateFormat>\n"); + xml += " </PublicKeyConfiguration>\n"; + xml += " <!-- The Key Exchange configuration, add or overide default key exchange implementations -->\n"; + xml += " <KeyExchangeConfiguration>\n"; + it = kexExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultKeyExchange + + "</DefaultAlgorithm>\n"); + xml += " </KeyExchangeConfiguration>\n"; + xml += " <!-- The Authentication configuration, add or overide default authentication implementations -->\n"; + xml += " <AuthenticationConfiguration>\n"; + it = authExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += " </AuthenticationConfiguration>\n"; + xml += "</SshAPIConfiguration>"; + + return xml; + } +} diff --git a/src/com/sshtools/common/configuration/SshToolsConnectionProfile.java b/src/com/sshtools/common/configuration/SshToolsConnectionProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..ad34f678767fe6e77fc9220a6409ebf8a26234c4 --- /dev/null +++ b/src/com/sshtools/common/configuration/SshToolsConnectionProfile.java @@ -0,0 +1,826 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.configuration; + +import com.sshtools.common.util.PropertyUtil; + +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClientFactory; +import com.sshtools.j2ssh.configuration.SshConnectionProperties; +import com.sshtools.j2ssh.forwarding.ForwardingConfiguration; +import com.sshtools.j2ssh.io.IOUtil; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.awt.Color; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class SshToolsConnectionProfile extends SshConnectionProperties { + private static Log log = LogFactory.getLog(SshToolsConnectionProfile.class); + + /** */ + public static final int DO_NOTHING = 1; + + /** */ + public static final int START_SHELL = 2; + + /** */ + public static final int EXECUTE_COMMANDS = 3; + private Map applicationProperties = new HashMap(); + private Map sftpFavorites = new HashMap(); + private Map authMethods = new HashMap(); + private boolean requestPseudoTerminal = true; + private boolean disconnectOnSessionClose = true; + private int onceAuthenticated = START_SHELL; + private String executeCommands = ""; + private boolean allowAgentForwarding = false; + + // SAX Processing variables + private String currentElement = null; + private String currentAuthentication = null; + private Properties currentProperties = null; + private String connectionFile; + + /** +* Creates a new SshToolsConnectionProfile object. +*/ + public SshToolsConnectionProfile() { + } + + /** +* +* +* @return +*/ + public Map getAuthenticationMethods() { + return authMethods; + } + + /** +* +* +* @return +*/ + public boolean requiresPseudoTerminal() { + return requestPseudoTerminal; + } + + /** +* +* +* @return +*/ + public boolean disconnectOnSessionClose() { + return disconnectOnSessionClose; + } + + /** +* +* +* @param requiresPseudoTerminal +*/ + public void setRequiresPseudoTerminal(boolean requiresPseudoTerminal) { + this.requestPseudoTerminal = requiresPseudoTerminal; + } + + /** +* +* +* @param disconnectOnSessionClose +*/ + public void setDisconnectOnSessionClose(boolean disconnectOnSessionClose) { + this.disconnectOnSessionClose = disconnectOnSessionClose; + } + + /** +* +*/ + public void clearAuthenticationCache() { + SshAuthenticationClient auth; + Properties properties; + + for (Iterator it = authMethods.values().iterator(); it.hasNext();) { + auth = (SshAuthenticationClient) it.next(); + properties = auth.getPersistableProperties(); + auth.reset(); + auth.setPersistableProperties(properties); + } + } + + /** +* +* +* @param onceAuthenticated +*/ + public void setOnceAuthenticatedCommand(int onceAuthenticated) { + this.onceAuthenticated = onceAuthenticated; + } + + /** +* +* +* @return +*/ + public int getOnceAuthenticatedCommand() { + return onceAuthenticated; + } + + /** +* +* +* @param executeCommands +*/ + public void setCommandsToExecute(String executeCommands) { + this.executeCommands = executeCommands; + } + + /** +* +* +* @return +*/ + public String getCommandsToExecute() { + return executeCommands; + } + + /** +* +* +* @param name +* @param defaultValue +* +* @return +*/ + public String getApplicationProperty(String name, String defaultValue) { + String value = (String) applicationProperties.get(name); + + if (value == null) { + return defaultValue; + } else { + return value; + } + } + + /** +* +* +* @param name +* @param defaultValue +* +* @return +*/ + public Map getSftpFavorites() { + return sftpFavorites; + } + + /** +* +* +* @param name +* @param defaultValue +* +* @return +*/ + public void setSftpFavorite(String name, String value) { + sftpFavorites.put(name, value); + } + + /** +* +* +* @param name +* @param defaultValue +* +* @return +*/ + public int getApplicationPropertyInt(String name, int defaultValue) { + try { + return Integer.parseInt(getApplicationProperty(name, + String.valueOf(defaultValue))); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** +* +* +* @param name +* @param defaultValue +* +* @return +*/ + public boolean getApplicationPropertyBoolean(String name, + boolean defaultValue) { + try { + return new Boolean(getApplicationProperty(name, + String.valueOf(defaultValue))).booleanValue(); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** +* +* +* @param name +* @param defaultColor +* +* @return +*/ + public Color getApplicationPropertyColor(String name, Color defaultColor) { + return PropertyUtil.stringToColor(getApplicationProperty(name, + PropertyUtil.colorToString(defaultColor))); + } + + /** +* +* +* @param name +* @param value +*/ + public void setApplicationProperty(String name, String value) { + applicationProperties.put(name, value); + } + + /** +* +* +* @param name +* @param value +*/ + public void setApplicationProperty(String name, int value) { + applicationProperties.put(name, String.valueOf(value)); + } + + /** +* +* +* @param name +* @param value +*/ + public void setApplicationProperty(String name, boolean value) { + applicationProperties.put(name, String.valueOf(value)); + } + + /** +* +* +* @param name +* @param value +*/ + public void setApplicationProperty(String name, Color value) { + applicationProperties.put(name, PropertyUtil.colorToString(value)); + } + + /** +* +* +* @param method +*/ + public void addAuthenticationMethod(SshAuthenticationClient method) { + if (method != null) { + if (!authMethods.containsKey(method.getMethodName())) { + authMethods.put(method.getMethodName(), method); + } + } + } + + /** +* +* +* @param config +*/ + public void addLocalForwarding(ForwardingConfiguration config) { + if (config != null) { + localForwardings.put(config.getName(), config); + } + } + + /** +* +* +* @param config +*/ + public void addRemoteForwarding(ForwardingConfiguration config) { + if (config != null) { + remoteForwardings.put(config.getName(), config); + } + } + + /** +* +* +* @return +*/ + public boolean getAllowAgentForwarding() { + return allowAgentForwarding; + } + + /** +* +* +* @param allowAgentForwarding +*/ + public void setAllowAgentForwarding(boolean allowAgentForwarding) { + this.allowAgentForwarding = allowAgentForwarding; + } + + /** +* +* +* @param name +*/ + public void removeLocalForwarding(String name) { + localForwardings.remove(name); + } + + /** +* +* +* @param name +*/ + public void removeRemoteForwarding(String name) { + remoteForwardings.remove(name); + } + + /** +* +* +* @param file +* +* @throws InvalidProfileFileException +*/ + public void open(String file) throws InvalidProfileFileException { + connectionFile = file; + open(new File(file)); + } + + /** +* +* +* @param file +* +* @throws InvalidProfileFileException +*/ + public void open(File file) throws InvalidProfileFileException { + InputStream in = null; + + try { + in = new FileInputStream(file); + open(in); + } catch (FileNotFoundException fnfe) { + throw new InvalidProfileFileException(file + " was not found!"); + } finally { + IOUtil.closeStream(in); + } + } + + /** +* +* +* @param in +* +* @throws InvalidProfileFileException +*/ + public void open(InputStream in) throws InvalidProfileFileException { + try { + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + XMLHandler handler = new XMLHandler(); + saxParser.parse(in, handler); + handler = null; + + // in.close(); + } catch (IOException ioe) { + throw new InvalidProfileFileException("IO error. " + + ioe.getMessage()); + } catch (SAXException sax) { + throw new InvalidProfileFileException("SAX Error: " + + sax.getMessage()); + } catch (ParserConfigurationException pce) { + throw new InvalidProfileFileException("SAX Parser Error: " + + pce.getMessage()); + } finally { + } + } + + /** +* +* +* @param method +*/ + public void removeAuthenticaitonMethod(String method) { + authMethods.remove(method); + } + + public void removeAuthenticationMethods() { + authMethods.clear(); + } + + /** +* +* +* @param file +* +* @throws InvalidProfileFileException +*/ + public void save(String file) throws InvalidProfileFileException { + try { + File f = new File(file); + FileOutputStream out = new FileOutputStream(f); + out.write(toString().getBytes()); + out.close(); + } catch (FileNotFoundException fnfe) { + throw new InvalidProfileFileException(file + " was not found!"); + } catch (IOException ioe) { + throw new InvalidProfileFileException("io error on " + file); + } finally { + } + } + + public void save() throws InvalidProfileFileException { + save(connectionFile); + } + + /** +* +* +* @return +*/ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += ("<SshToolsConnectionProfile Hostname=\"" + host + "\" Port=\"" + + String.valueOf(port) + "\" Username=\"" + username + "\"" + + " Provider=\"" + getTransportProviderString() + "\">"); + xml += (" <PreferedCipher Client2Server=\"" + prefEncryption + + "\" Server2Client=\"" + prefDecryption + "\"/>\n"); + xml += (" <PreferedMac Client2Server=\"" + prefRecvMac + + "\" Server2Client=\"" + prefSendMac + "\"/>\n"); + xml += (" <PreferedCompression Client2Server=\"" + prefRecvComp + + "\" Server2Client=\"" + prefSendComp + "\"/>\n"); + xml += (" <PreferedPublicKey Name=\"" + prefPK + "\"/>\n"); + xml += (" <PreferedKeyExchange Name=\"" + prefKex + "\"/>\n"); + xml += (" <OnceAuthenticated value=\"" + + String.valueOf(onceAuthenticated) + "\"/>\n"); + + if (onceAuthenticated == EXECUTE_COMMANDS) { + xml += (" <ExecuteCommands>" + executeCommands + + "</ExecuteCommands>\n"); + } + + Iterator it = authMethods.entrySet().iterator(); + Map.Entry entry; + Properties properties; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + xml += (" <AuthenticationMethod Name=\"" + entry.getKey() + + "\">\n"); + + SshAuthenticationClient auth = (SshAuthenticationClient) entry.getValue(); + properties = auth.getPersistableProperties(); + + Iterator it2 = properties.entrySet().iterator(); + + while (it2.hasNext()) { + entry = (Map.Entry) it2.next(); + xml += (" <AuthenticationProperty Name=\"" + + entry.getKey() + "\" Value=\"" + entry.getValue() + "\"/>\n"); + } + + xml += " </AuthenticationMethod>\n"; + } + + it = applicationProperties.entrySet().iterator(); + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + xml += (" <ApplicationProperty Name=\"" + entry.getKey() + + "\" Value=\"" + entry.getValue() + "\"/>\n"); + } + + // Write the SFTP Favorite entries to file + it = sftpFavorites.entrySet().iterator(); + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + xml += (" <SftpFavorite Name=\"" + entry.getKey() + + "\" Value=\"" + entry.getValue() + "\"/>\n"); + } + + it = localForwardings.values().iterator(); + xml += (" <ForwardingAutoStart value=\"" + + String.valueOf(forwardingAutoStart) + "\"/>\n"); + + while (it.hasNext()) { + ForwardingConfiguration config = (ForwardingConfiguration) it.next(); + xml += (" <LocalPortForwarding Name=\"" + config.getName() + + "\" AddressToBind=\"" + config.getAddressToBind() + + "\" PortToBind=\"" + String.valueOf(config.getPortToBind()) + + "\" AddressToConnect=\"" + config.getHostToConnect() + + "\" PortToConnect=\"" + String.valueOf(config.getPortToConnect()) + + "\"/>\n"); + } + + it = remoteForwardings.values().iterator(); + + while (it.hasNext()) { + ForwardingConfiguration config = (ForwardingConfiguration) it.next(); + xml += (" <RemotePortForwarding Name=\"" + config.getName() + + "\" AddressToBind=\"" + config.getAddressToBind() + + "\" PortToBind=\"" + String.valueOf(config.getPortToBind()) + + "\" AddressToConnect=\"" + config.getHostToConnect() + + "\" PortToConnect=\"" + String.valueOf(config.getPortToConnect()) + + "\"/>\n"); + } + + xml += "</SshToolsConnectionProfile>"; + + return xml; + } + + private class XMLHandler extends DefaultHandler { + boolean commandsToExecute = false; + + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (!qname.equals("SshToolsConnectionProfile")) { + throw new SAXException("Unexpected root element " + qname); + } + + host = attrs.getValue("Hostname"); + username = attrs.getValue("Username"); + + String p = attrs.getValue("Port"); + + if (p == null) { + port = 22; + } else { + port = Integer.parseInt(p); + } + + setTransportProviderString(attrs.getValue("Provider")); + + if ((host == null) || (username == null)) { + throw new SAXException( + "Required attribute for element <SshToolsConnectionProfile> missing"); + } + } else { + String c2s; + String s2c; + + if (currentElement.equals("SshToolsConnectionProfile")) { + if (qname.equals("PreferedCipher")) { + c2s = attrs.getValue("Client2Server"); + s2c = attrs.getValue("Server2Client"); + + if ((c2s == null) || (s2c == null)) { + throw new SAXException( + "Required attribute missing for <PreferedCipher> element"); + } + + prefEncryption = c2s; + prefDecryption = s2c; + } else if (qname.equals("OnceAuthenticated")) { + if (attrs.getValue("value") != null) { + try { + onceAuthenticated = Integer.parseInt(attrs.getValue( + "value")); + } catch (NumberFormatException ex) { + onceAuthenticated = START_SHELL; + } + } + } else if (qname.equals("ExecuteCommands")) { + commandsToExecute = true; + executeCommands = ""; + } else if (qname.equals("PreferedCompression")) { + c2s = attrs.getValue("Client2Server"); + s2c = attrs.getValue("Server2Client"); + + if ((c2s == null) || (s2c == null)) { + throw new SAXException( + "Required attribute missing for <PreferedCompression> element"); + } + + prefRecvComp = c2s; + prefSendComp = s2c; + } else if (qname.equals("PreferedMac")) { + c2s = attrs.getValue("Client2Server"); + s2c = attrs.getValue("Server2Client"); + + if ((c2s == null) || (s2c == null)) { + throw new SAXException( + "Required attribute missing for <PreferedMac> element"); + } + + prefRecvMac = c2s; + prefSendMac = s2c; + } else if (qname.equals("PreferedPublicKey")) { + String name = attrs.getValue("Name"); + + if (name == null) { + throw new SAXException( + "Required attribute missing for <PreferedPublickey> element"); + } + + prefPK = name; + } else if (qname.equals("PreferedKeyExchange")) { + String name = attrs.getValue("Name"); + + if (name == null) { + throw new SAXException( + "Required attribute missing for <PreferedKeyExchange> element"); + } + + prefPK = name; + } else if (qname.equals("ApplicationProperty")) { + String name = attrs.getValue("Name"); + String value = attrs.getValue("Value"); + + if ((name == null) || (value == null)) { + throw new SAXException( + "Required attributes missing for <ApplicationProperty> element"); + } + + applicationProperties.put(name, value); + } else if (qname.equals("SftpFavorite")) { + String name = attrs.getValue("Name"); + String value = attrs.getValue("Value"); + + if ((name == null) || (value == null)) { + throw new SAXException( + "Required attributes missing for <SftpFavorite> element"); + } + + sftpFavorites.put(name, value); + } else if (qname.equals("AuthenticationMethod")) { + currentAuthentication = attrs.getValue("Name"); + currentProperties = new Properties(); + + if (currentAuthentication == null) { + throw new SAXException( + "Required attribute missing for <AuthenticationMethod> element"); + } + } else if (qname.equals("LocalPortForwarding") || + qname.equals("RemotePortForwarding")) { + String name = attrs.getValue("Name"); + String addressToBind = attrs.getValue("AddressToBind"); + String portToBind = attrs.getValue("PortToBind"); + String addressToConnect = attrs.getValue( + "AddressToConnect"); + String portToConnect = attrs.getValue("PortToConnect"); + + if ((name == null) || (addressToBind == null) || + (portToBind == null) || + (addressToConnect == null) || + (portToConnect == null)) { + throw new SAXException( + "Required attribute missing for <" + qname + + "> element"); + } + + ForwardingConfiguration config = new ForwardingConfiguration(name, + addressToBind, Integer.parseInt(portToBind), + addressToConnect, + Integer.parseInt(portToConnect)); + + if (qname.equals("LocalPortForwarding")) { + localForwardings.put(name, config); + } else { + remoteForwardings.put(name, config); + } + } else if (qname.equals("ForwardingAutoStart")) { + try { + forwardingAutoStart = Boolean.valueOf(attrs.getValue( + "value")).booleanValue(); + } catch (Throwable ex1) { + } + } else { + throw new SAXException("Unexpected element <" + qname + + "> after SshToolsConnectionProfile"); + } + } else if (currentElement.equals("AuthenticationMethod")) { + if (qname.equals("AuthenticationProperty")) { + String name = attrs.getValue("Name"); + String value = attrs.getValue("Value"); + + if ((name == null) || (value == null)) { + throw new SAXException( + "Required attribute missing for <AuthenticationProperty> element"); + } + + currentProperties.setProperty(name, value); + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after AuthenticationMethod"); + } + } + } + + currentElement = qname; + } + + public void characters(char[] ch, int pos, int len) { + executeCommands += new String(ch, pos, len); + } + + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement != null) { + if (!currentElement.equals(qname)) { + throw new SAXException("Unexpected end element found " + + qname); + } else if (qname.equals("SshToolsConnectionProfile")) { + currentElement = null; + } else if (qname.startsWith("Prefered")) { + currentElement = "SshToolsConnectionProfile"; + } else if (qname.equals("OnceAuthenticated")) { + currentElement = "SshToolsConnectionProfile"; + } else if (qname.equals("ExecuteCommands")) { + currentElement = "SshToolsConnectionProfile"; + } else if (qname.equals("ApplicationProperty")) { + currentElement = "SshToolsConnectionProfile"; + } else if (qname.equals("SftpFavorite")) { + currentElement = "SshToolsConnectionProfile"; + } else if (qname.equals("AuthenticationProperty")) { + currentElement = "AuthenticationMethod"; + } else if (qname.equals("LocalPortForwarding") || + qname.equals("RemotePortForwarding") || + qname.equals("ForwardingAutoStart")) { + currentElement = "SshToolsConnectionProfile"; + } else if (qname.equals("AuthenticationMethod")) { + currentElement = "SshToolsConnectionProfile"; + + try { + SshAuthenticationClient auth = SshAuthenticationClientFactory.newInstance(currentAuthentication); + auth.setPersistableProperties(currentProperties); + authMethods.put(currentAuthentication, auth); + } catch (AlgorithmNotSupportedException anse) { + log.warn( + "AuthenticationMethod element ignored because '" + + currentAuthentication + + "' authentication is not supported"); + } finally { + currentAuthentication = null; + } + } else { + throw new SAXException("Unexpected end element <" + qname + + "> found"); + } + } + } + } +} diff --git a/src/com/sshtools/common/configuration/XmlConfigurationContext.java b/src/com/sshtools/common/configuration/XmlConfigurationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..a5f101a926c3ec2a4f75d4535e7dff33cc6bf441 --- /dev/null +++ b/src/com/sshtools/common/configuration/XmlConfigurationContext.java @@ -0,0 +1,150 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.configuration; + +import com.sshtools.common.automate.*; + +import com.sshtools.j2ssh.configuration.*; + +import org.apache.commons.logging.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class XmlConfigurationContext implements ConfigurationContext { + private static Log log = LogFactory.getLog(XmlConfigurationContext.class); + HashMap configurations = new HashMap(); + String apiResource = "sshtools.xml"; + String automationResource = "automation.xml"; + private boolean failOnError = false; + + /** +* Creates a new XmlConfigurationContext object. +*/ + public XmlConfigurationContext() { + } + + /** +* +* +* @param apiResource +*/ + public void setAPIConfigurationResource(String apiResource) { + this.apiResource = apiResource; + } + + /** +* +* +* @param automationResource +*/ + public void setAutomationConfigurationResource(String automationResource) { + this.automationResource = automationResource; + } + + /** +* +* +* @param failOnError +*/ + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } + + /** +* +* +* @throws ConfigurationException +*/ + public void initialize() throws ConfigurationException { + if (apiResource != null) { + try { + SshAPIConfiguration x = new SshAPIConfiguration(ConfigurationLoader.loadFile( + apiResource)); + configurations.put(com.sshtools.j2ssh.configuration.SshAPIConfiguration.class, + x); + } catch (Exception ex) { + if (failOnError) { + throw new ConfigurationException(ex.getMessage()); + } else { + log.info(apiResource + " could not be found: " + + ex.getMessage()); + } + } + } + + if (automationResource != null) { + try { + AutomationConfiguration y = new AutomationConfiguration(ConfigurationLoader.loadFile( + automationResource)); + configurations.put(com.sshtools.common.automate.AutomationConfiguration.class, + y); + } catch (Exception ex) { + if (failOnError) { + throw new ConfigurationException(ex.getMessage()); + } else { + log.info(automationResource + " could not be found: " + + ex.getMessage()); + } + } + } + } + + /** +* +* +* @param cls +* +* @return +*/ + public boolean isConfigurationAvailable(Class cls) { + return configurations.containsKey(cls); + } + + /** +* +* +* @param cls +* +* @return +* +* @throws ConfigurationException +*/ + public Object getConfiguration(Class cls) throws ConfigurationException { + if (configurations.containsKey(cls)) { + return configurations.get(cls); + } else { + throw new ConfigurationException(cls.getName() + + " configuration not available"); + } + } +} diff --git a/src/com/sshtools/common/hosts/AbstractHostKeyVerification.java b/src/com/sshtools/common/hosts/AbstractHostKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..f75a3dceabe0e4965c13f64deb358a13fa11e4c1 --- /dev/null +++ b/src/com/sshtools/common/hosts/AbstractHostKeyVerification.java @@ -0,0 +1,524 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.hosts; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.transport.HostKeyVerification; +import com.sshtools.j2ssh.transport.InvalidHostFileException; +import com.sshtools.j2ssh.transport.TransportProtocolException; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; + +import java.security.AccessControlException; +import java.security.AccessController; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public abstract class AbstractHostKeyVerification extends DefaultHandler + implements HostKeyVerification { + private static String defaultHostFile; + private static Log log = LogFactory.getLog(HostKeyVerification.class); + + static { + log.info("Determining default host file"); + + // Get the sshtools.home system property + defaultHostFile = ConfigurationLoader.getConfigurationDirectory(); + + if (defaultHostFile == null) { + log.info( + "No configuration location, persistence of host keys will be disabled."); + } else { + // Set the default host file name to our hosts.xml + defaultHostFile += "hosts.xml"; + log.info("Defaulting host file to " + defaultHostFile); + } + } + + private List deniedHosts = new ArrayList(); + private Map allowedHosts = new HashMap(); + private String hostFile; + private boolean hostFileWriteable; + private boolean expectEndElement = false; + private String currentElement = null; + + /** +* Creates a new AbstractHostKeyVerification object. +* +* @throws InvalidHostFileException +*/ + public AbstractHostKeyVerification() throws InvalidHostFileException { + this(defaultHostFile); + hostFile = defaultHostFile; + } + + /** +* Creates a new AbstractHostKeyVerification object. +* +* @param hostFileName +* +* @throws InvalidHostFileException +*/ + public AbstractHostKeyVerification(String hostFileName) + throws InvalidHostFileException { + InputStream in = null; + + try { + // If no host file is supplied, or there is not enough permission to load + // the file, then just create an empty list. + if (hostFileName != null) { + if (System.getSecurityManager() != null) { + AccessController.checkPermission(new FilePermission( + hostFileName, "read")); + } + + // Load the hosts file. Do not worry if fle doesnt exist, just disable + // save of + File f = new File(hostFileName); + + if (f.exists()) { + in = new FileInputStream(f); + hostFile = hostFileName; + + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + saxParser.parse(in, this); + hostFileWriteable = f.canWrite(); + } else { + // Try to create the file + if (f.createNewFile()) { + FileOutputStream out = new FileOutputStream(f); + out.write(toString().getBytes()); + out.close(); + hostFileWriteable = true; + } else { + hostFileWriteable = false; + } + } + + if (!hostFileWriteable) { + log.warn("Host file is not writeable."); + } + } + } catch (AccessControlException ace) { + log.warn( + "Not enough permission to load a hosts file, so just creating an empty list"); + } catch (IOException ioe) { + throw new InvalidHostFileException("Could not open or read " + + hostFileName); + } catch (SAXException sax) { + throw new InvalidHostFileException("Failed XML parsing: " + + sax.getMessage()); + } catch (ParserConfigurationException pce) { + throw new InvalidHostFileException( + "Failed to initialize xml parser: " + pce.getMessage()); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ioe) { + } + } + } + } + + /** +* +* +* @param uri +* @param localName +* @param qname +* @param attrs +* +* @throws SAXException +*/ + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (qname.equals("HostAuthorizations")) { + allowedHosts.clear(); + deniedHosts.clear(); + currentElement = qname; + } else { + throw new SAXException("Unexpected document element!"); + } + } else { + if (!currentElement.equals("HostAuthorizations")) { + throw new SAXException("Unexpected parent element found!"); + } + + if (qname.equals("AllowHost")) { + String hostname = attrs.getValue("HostName"); + String fingerprint = attrs.getValue("Fingerprint"); + + if ((hostname != null) && (fingerprint != null)) { + if (log.isDebugEnabled()) { + log.debug("AllowHost element for host '" + hostname + + "' with fingerprint '" + fingerprint + "'"); + } + + allowedHosts.put(hostname, fingerprint); + currentElement = qname; + } else { + throw new SAXException("Requried attribute(s) missing!"); + } + } else if (qname.equals("DenyHost")) { + String hostname = attrs.getValue("HostName"); + + if (hostname != null) { + if (log.isDebugEnabled()) { + log.debug("DenyHost element for host " + hostname); + } + + deniedHosts.add(hostname); + currentElement = qname; + } else { + throw new SAXException( + "Required attribute hostname missing"); + } + } else { + log.warn("Unexpected " + qname + + " element found in allowed hosts file"); + } + } + } + + /** +* +* +* @param uri +* @param localName +* @param qname +* +* @throws SAXException +*/ + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement == null) { + throw new SAXException("Unexpected end element found!"); + } + + if (currentElement.equals("HostAuthorizations")) { + currentElement = null; + + return; + } + + if (currentElement.equals("AllowHost")) { + currentElement = "HostAuthorizations"; + + return; + } + + if (currentElement.equals("DenyHost")) { + currentElement = "HostAuthorizations"; + + return; + } + } + + /** +* +* +* @return +*/ + public boolean isHostFileWriteable() { + return hostFileWriteable; + } + + /** +* +* +* @param host +* +* @throws TransportProtocolException +*/ + public abstract void onDeniedHost(String host) + throws TransportProtocolException; + + /** +* +* +* @param host +* @param allowedHostKey +* @param actualHostKey +* +* @throws TransportProtocolException +*/ + public abstract void onHostKeyMismatch(String host, String allowedHostKey, + String actualHostKey) throws TransportProtocolException; + + /** +* +* +* @param host +* @param hostKeyFingerprint +* +* @throws TransportProtocolException +*/ + public abstract void onUnknownHost(String host, String hostKeyFingerprint) + throws TransportProtocolException; + + /** +* +* +* @param host +* @param hostKeyFingerprint +* @param always +* +* @throws InvalidHostFileException +*/ + public void allowHost(String host, String hostKeyFingerprint, boolean always) + throws InvalidHostFileException { + if (log.isDebugEnabled()) { + log.debug("Allowing " + host + " with fingerprint " + + hostKeyFingerprint); + } + + // Put the host into the allowed hosts list, overiding any previous + // entry + allowedHosts.put(host, hostKeyFingerprint); + + // If we always want to allow then save the host file with the + // new details + if (always) { + saveHostFile(); + } + } + + /** +* +* +* @return +*/ + public Map allowedHosts() { + return allowedHosts; + } + + /** +* +* +* @return +*/ + public java.util.List deniedHosts() { + return deniedHosts; + } + + /** +* +* +* @param host +*/ + public void removeAllowedHost(String host) { + allowedHosts.remove(host); + } + + /** +* +* +* @param host +*/ + public void removeDeniedHost(String host) { + for (int i = deniedHosts.size() - 1; i >= 0; i--) { + String h = (String) deniedHosts.get(i); + + if (h.equals(host)) { + deniedHosts.remove(i); + } + } + } + + /** +* +* +* @param host +* @param always +* +* @throws InvalidHostFileException +*/ + public void denyHost(String host, boolean always) + throws InvalidHostFileException { + if (log.isDebugEnabled()) { + log.debug(host + " is denied access"); + } + + // Get the denied host from the list + if (!deniedHosts.contains(host)) { + deniedHosts.add(host); + } + + // Save it if need be + if (always) { + saveHostFile(); + } + } + + /** +* +* +* @param host +* @param pk +* +* @return +* +* @throws TransportProtocolException +*/ + public boolean verifyHost(String host, SshPublicKey pk) + throws TransportProtocolException { + String fingerprint = pk.getFingerprint(); + log.info("Verifying " + host + " host key"); + + if (log.isDebugEnabled()) { + log.debug("Fingerprint: " + fingerprint); + } + + // See if the host is denied by looking at the denied hosts list + if (deniedHosts.contains(host)) { + onDeniedHost(host); + + return false; + } + + // Try the allowed hosts by looking at the allowed hosts map + if (allowedHosts.containsKey(host)) { + // The host is allowed so check the fingerprint + String currentFingerprint = (String) allowedHosts.get(host); + + if (currentFingerprint.compareToIgnoreCase(fingerprint) == 0) { + return true; + } + + // The host key does not match the recorded so call the abstract + // method so that the user can decide + onHostKeyMismatch(host, currentFingerprint, fingerprint); + + // Recheck the after the users input + return checkFingerprint(host, fingerprint); + } else { + // The host is unknown os ask the user + onUnknownHost(host, fingerprint); + + // Recheck ans return the result + return checkFingerprint(host, fingerprint); + } + } + + private boolean checkFingerprint(String host, String fingerprint) { + String currentFingerprint = (String) allowedHosts.get(host); + + if (currentFingerprint != null) { + if (currentFingerprint.compareToIgnoreCase(fingerprint) == 0) { + return true; + } + } + + return false; + } + + /** +* +* +* @throws InvalidHostFileException +*/ + public void saveHostFile() throws InvalidHostFileException { + if (!hostFileWriteable) { + throw new InvalidHostFileException("Host file is not writeable."); + } + + log.info("Saving " + defaultHostFile); + + try { + File f = new File(hostFile); + FileOutputStream out = new FileOutputStream(f); + out.write(toString().getBytes()); + out.close(); + } catch (IOException e) { + throw new InvalidHostFileException("Could not write to " + + hostFile); + } + } + + /** +* +* +* @return +*/ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<HostAuthorizations>\n"; + xml += "<!-- Host Authorizations file, used by the abstract class HostKeyVerification to verify the servers host key -->"; + xml += " <!-- Allow the following hosts access if they provide the correct public key -->\n"; + + Map.Entry entry; + Iterator it = allowedHosts.entrySet().iterator(); + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + xml += (" " + "<AllowHost HostName=\"" + + entry.getKey().toString() + "\" Fingerprint=\"" + + entry.getValue().toString() + "\"/>\n"); + } + + xml += " <!-- Deny the following hosts access -->\n"; + it = deniedHosts.iterator(); + + while (it.hasNext()) { + xml += (" <DenyHost HostName=\"" + it.next().toString() + + "\"/>\n"); + } + + xml += "</HostAuthorizations>"; + + return xml; + } +} diff --git a/src/com/sshtools/common/hosts/ConsoleHostKeyVerification.java b/src/com/sshtools/common/hosts/ConsoleHostKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..9531c80b1336c85517054471b0e816d77074bc29 --- /dev/null +++ b/src/com/sshtools/common/hosts/ConsoleHostKeyVerification.java @@ -0,0 +1,140 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.hosts; + +import com.sshtools.j2ssh.transport.InvalidHostFileException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class ConsoleHostKeyVerification extends AbstractHostKeyVerification { + /** +* Creates a new ConsoleHostKeyVerification object. +* +* @throws InvalidHostFileException +*/ + public ConsoleHostKeyVerification() throws InvalidHostFileException { + super(); + } + + /** +* Creates a new ConsoleHostKeyVerification object. +* +* @param hostFile +* +* @throws InvalidHostFileException +*/ + public ConsoleHostKeyVerification(String hostFile) + throws InvalidHostFileException { + super(hostFile); + } + + /** +* +* +* @param hostname +*/ + public void onDeniedHost(String hostname) { + System.out.println("Access to the host " + hostname + + " is denied from this system"); + } + + /** +* +* +* @param host +* @param fingerprint +* @param actual +*/ + public void onHostKeyMismatch(String host, String fingerprint, String actual) { + try { + System.out.println("The host key supplied by " + host + " is: " + + actual); + System.out.println("The current allowed key for " + host + " is: " + + fingerprint); + getResponse(host, actual); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** +* +* +* @param host +* @param fingerprint +*/ + public void onUnknownHost(String host, String fingerprint) { + try { + System.out.println("The host " + host + + " is currently unknown to the system"); + System.out.println("The host key fingerprint is: " + fingerprint); + getResponse(host, fingerprint); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void getResponse(String host, String fingerprint) + throws InvalidHostFileException, IOException { + String response = ""; + BufferedReader reader = new BufferedReader(new InputStreamReader( + System.in)); + + while (!(response.equalsIgnoreCase("YES") || + response.equalsIgnoreCase("NO") || + (response.equalsIgnoreCase("ALWAYS") && isHostFileWriteable()))) { + String options = (isHostFileWriteable() ? "Yes|No|Always" : "Yes|No"); + + if (!isHostFileWriteable()) { + System.out.println( + "Always option disabled, host file is not writeable"); + } + + System.out.print("Do you want to allow this host key? [" + options + + "]: "); + response = reader.readLine(); + } + + if (response.equalsIgnoreCase("YES")) { + allowHost(host, fingerprint, false); + } + + if (response.equalsIgnoreCase("ALWAYS") && isHostFileWriteable()) { + allowHost(host, fingerprint, true); + } + + // Do nothing on NO + } +} diff --git a/src/com/sshtools/common/hosts/DialogHostKeyVerification.java b/src/com/sshtools/common/hosts/DialogHostKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..74151722931fc5c58c6a898130879a42f4ea88fa --- /dev/null +++ b/src/com/sshtools/common/hosts/DialogHostKeyVerification.java @@ -0,0 +1,235 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.hosts; + +import com.sshtools.j2ssh.transport.InvalidHostFileException; +import com.sshtools.j2ssh.transport.TransportProtocolException; + +import java.awt.Component; + +import java.lang.reflect.InvocationTargetException; + +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class DialogHostKeyVerification extends AbstractHostKeyVerification { + Component parent; + private boolean verificationEnabled = true; + + /** +* Creates a new DialogHostKeyVerification object. +* +* @param parent +* +* @throws InvalidHostFileException +*/ + public DialogHostKeyVerification(Component parent) + throws InvalidHostFileException { + this.parent = parent; + } + + /** +* Creates a new DialogHostKeyVerification object. +* +* @param parent +* @param hostFileName +* +* @throws InvalidHostFileException +*/ + public DialogHostKeyVerification(Component parent, String hostFileName) + throws InvalidHostFileException { + super(hostFileName); + this.parent = parent; + } + + /** +* +* +* @param enabled +*/ + public void setVerificationEnabled(boolean enabled) { + this.verificationEnabled = verificationEnabled; + } + + /** +* +* +* @param host +* +* @throws TransportProtocolException +*/ + public void onDeniedHost(final String host) + throws TransportProtocolException { + // Show a message to the user to inform them that the host + // is denied + try { + if (verificationEnabled) { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + JOptionPane.showMessageDialog(parent, + "Access to '" + host + "' is denied.\n" + + "Verify the access granted/denied in the allowed hosts file.", + "Remote Host Authentication", + JOptionPane.ERROR_MESSAGE); + } + }); + } + } catch (InvocationTargetException ite) { + throw new TransportProtocolException("Invocation Exception: " + + ite.getMessage()); + } catch (InterruptedException ie) { + throw new TransportProtocolException( + "SwingUtilities thread interrupted!"); + } + } + + /** +* +* +* @param host +* @param recordedFingerprint +* @param actualFingerprint +* +* @throws TransportProtocolException +*/ + public void onHostKeyMismatch(final String host, + final String recordedFingerprint, final String actualFingerprint) + throws TransportProtocolException { + try { + if (verificationEnabled) { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + Object[] options = getOptions(); + int res = JOptionPane.showOptionDialog(parent, + "The host '" + host + + "' has provided a different host key.\nThe host key" + + " fingerprint provided is '" + + actualFingerprint + "'.\n" + + "The allowed host key fingerprint is " + + recordedFingerprint + + ".\nDo you want to allow this host?", + "Remote host authentication", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, + options, options[0]); + + try { + // Handle the reply + if ((options.length == 3) && (res == 0)) { + // Always allow the host with the new fingerprint + allowHost(host, actualFingerprint, true); + } else if (((options.length == 2) && + (res == 0)) || + ((options.length == 3) && (res == 1))) { + // Only allow the host this once + allowHost(host, actualFingerprint, false); + } + } catch (InvalidHostFileException e) { + showExceptionMessage(e); + } + } + }); + } + } catch (InvocationTargetException ite) { + throw new TransportProtocolException("Invocation Exception: " + + ite.getMessage()); + } catch (InterruptedException ie) { + throw new TransportProtocolException( + "SwingUtilities thread interrupted!"); + } + } + + /** +* +* +* @param host +* @param fingerprint +* +* @throws TransportProtocolException +*/ + public void onUnknownHost(final String host, final String fingerprint) + throws TransportProtocolException { + // Set up the users options. Only allow always if we can + // write to the hosts file + try { + if (verificationEnabled) { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + Object[] options = getOptions(); + int res = JOptionPane.showOptionDialog(parent, + "The host '" + host + + "' is unknown. The host key" + + " fingerprint is\n'" + fingerprint + + "'.\nDo you want to allow this host?", + "Remote host authentication", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, + options, options[0]); + + try { + // Handle the reply + if ((options.length == 3) && (res == 0)) { + // Always allow the host with the new fingerprint + allowHost(host, fingerprint, true); + } else if (((options.length == 2) && + (res == 0)) || + ((options.length == 3) && (res == 1))) { + // Only allow the host this once + allowHost(host, fingerprint, false); + } + } catch (InvalidHostFileException e) { + showExceptionMessage(e); + } + } + }); + } + } catch (InvocationTargetException ite) { + throw new TransportProtocolException("Invocation Exception: " + + ite.getMessage()); + } catch (InterruptedException ie) { + throw new TransportProtocolException( + "SwingUtilities thread interrupted!"); + } + } + + private String[] getOptions() { + return isHostFileWriteable() ? new String[] { "Always", "Yes", "No" } + : new String[] { "Yes", "No" }; + } + + private void showExceptionMessage(Exception e) { + JOptionPane.showMessageDialog(parent, + "An unexpected error occured!\n\n" + e.getMessage(), + "Host Verification", JOptionPane.ERROR_MESSAGE); + } +} diff --git a/src/com/sshtools/common/hosts/DialogKnownHostsKeyVerification.java b/src/com/sshtools/common/hosts/DialogKnownHostsKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..fa0fa54d3122673a5c6348acfe4035928b367a15 --- /dev/null +++ b/src/com/sshtools/common/hosts/DialogKnownHostsKeyVerification.java @@ -0,0 +1,243 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.hosts; + +import com.sshtools.j2ssh.transport.AbstractKnownHostsKeyVerification; +import com.sshtools.j2ssh.transport.InvalidHostFileException; +import com.sshtools.j2ssh.transport.TransportProtocolException; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.awt.Component; + +import java.io.File; + +import java.lang.reflect.InvocationTargetException; + +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class DialogKnownHostsKeyVerification + extends AbstractKnownHostsKeyVerification { + Component parent; + private boolean verificationEnabled = true; + + /** +* Creates a new DialogKnownHostsKeyVerification object. +* +* @param parent +* +* @throws InvalidHostFileException +*/ + public DialogKnownHostsKeyVerification(Component parent) + throws InvalidHostFileException { + super(new File(System.getProperty("user.home"), + ".ssh" + File.separator + "known_hosts").getAbsolutePath()); + this.parent = parent; + } + + /** +* Creates a new DialogKnownHostsKeyVerification object. +* +* @param parent +* @param hostFileName +* +* @throws InvalidHostFileException +*/ + public DialogKnownHostsKeyVerification(Component parent, String hostFileName) + throws InvalidHostFileException { + super(hostFileName); + this.parent = parent; + } + + /** +* +* +* @param enabled +*/ + public void setVerificationEnabled(boolean enabled) { + this.verificationEnabled = verificationEnabled; + } + + /** +* +* +* @param host +* +* @throws TransportProtocolException +*/ + public void onDeniedHost(final String host) + throws TransportProtocolException { + // Show a message to the user to inform them that the host + // is denied + try { + if (verificationEnabled) { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + JOptionPane.showMessageDialog(parent, + "Access to '" + host + "' is denied.\n" + + "Verify the access granted/denied in the allowed hosts file.", + "Remote Host Authentication", + JOptionPane.ERROR_MESSAGE); + } + }); + } + } catch (InvocationTargetException ite) { + throw new TransportProtocolException("Invocation Exception: " + + ite.getMessage()); + } catch (InterruptedException ie) { + throw new TransportProtocolException( + "SwingUtilities thread interrupted!"); + } + } + + /** +* +* +* @param host +* @param recorded +* @param actual +* +* @throws TransportProtocolException +*/ + public void onHostKeyMismatch(final String host, + final SshPublicKey recorded, final SshPublicKey actual) + throws TransportProtocolException { + try { + if (verificationEnabled) { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + Object[] options = getOptions(); + int res = JOptionPane.showOptionDialog(parent, + "The host '" + host + + "' has provided a different host key.\nThe host key" + + " fingerprint provided is '" + + actual.getFingerprint() + "'.\n" + + "The allowed host key fingerprint is " + + recorded.getFingerprint() + + ".\nDo you want to allow this host?", + "Remote host authentication", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, + options, options[0]); + + try { + // Handle the reply + if ((options.length == 3) && (res == 0)) { + // Always allow the host with the new fingerprint + allowHost(host, actual, true); + } else if (((options.length == 2) && + (res == 0)) || + ((options.length == 3) && (res == 1))) { + // Only allow the host this once + allowHost(host, actual, false); + } + } catch (InvalidHostFileException e) { + showExceptionMessage(e); + } + } + }); + } + } catch (InvocationTargetException ite) { + throw new TransportProtocolException("Invocation Exception: " + + ite.getMessage()); + } catch (InterruptedException ie) { + throw new TransportProtocolException( + "SwingUtilities thread interrupted!"); + } + } + + /** +* +* +* @param host +* @param key +* +* @throws TransportProtocolException +*/ + public void onUnknownHost(final String host, final SshPublicKey key) + throws TransportProtocolException { + // Set up the users options. Only allow always if we can + // write to the hosts file + try { + if (verificationEnabled) { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + Object[] options = getOptions(); + int res = JOptionPane.showOptionDialog(parent, + "The host '" + host + + "' is unknown. The host key" + + " fingerprint is\n'" + + key.getFingerprint() + + "'.\nDo you want to allow this host?", + "Remote host authentication", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, + options, options[0]); + + try { + // Handle the reply + if ((options.length == 3) && (res == 0)) { + // Always allow the host with the new fingerprint + allowHost(host, key, true); + } else if (((options.length == 2) && + (res == 0)) || + ((options.length == 3) && (res == 1))) { + // Only allow the host this once + allowHost(host, key, false); + } + } catch (InvalidHostFileException e) { + showExceptionMessage(e); + } + } + }); + } + } catch (InvocationTargetException ite) { + throw new TransportProtocolException("Invocation Exception: " + + ite.getMessage()); + } catch (InterruptedException ie) { + throw new TransportProtocolException( + "SwingUtilities thread interrupted!"); + } + } + + private String[] getOptions() { + return isHostFileWriteable() ? new String[] { "Always", "Yes", "No" } + : new String[] { "Yes", "No" }; + } + + private void showExceptionMessage(Exception e) { + JOptionPane.showMessageDialog(parent, + "An unexpected error occured!\n\n" + e.getMessage(), + "Host Verification", JOptionPane.ERROR_MESSAGE); + } +} diff --git a/src/com/sshtools/common/keygen/KeygenPanel.java b/src/com/sshtools/common/keygen/KeygenPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..7d37085de3154e79e01dd368223973109ce2e9c3 --- /dev/null +++ b/src/com/sshtools/common/keygen/KeygenPanel.java @@ -0,0 +1,418 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.keygen; + +import com.sshtools.common.ui.NumericTextField; +import com.sshtools.common.ui.UIUtil; +import com.sshtools.common.ui.XTextField; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.io.File; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JProgressBar; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class KeygenPanel extends JPanel implements DocumentListener, + ActionListener { + /** */ + public final static int GENERATE_KEY_PAIR = 0; + + /** */ + public final static int CONVERT_IETF_SECSH_TO_OPENSSH = 1; + + /** */ + public final static int CONVERT_OPENSSH_TO_IETF_SECSH = 2; + + /** */ + public final static int CHANGE_PASSPHRASE = 3; + + // Private instance variables + private JButton browseInput; + + // Private instance variables + private JButton browseOutput; + private JComboBox action; + private JComboBox type; + private JLabel bitsLabel; + private JLabel inputFileLabel; + private JLabel newPassphraseLabel; + private JLabel oldPassphraseLabel; + private JLabel outputFileLabel; + private JLabel typeLabel; + private JPasswordField newPassphrase; + private JPasswordField oldPassphrase; + private JProgressBar strength; + private XTextField inputFile; + private XTextField outputFile; + private NumericTextField bits; + + /** +* Creates a new KeygenPanel object. +*/ + public KeygenPanel() { + super(); + + JPanel keyPanel = new JPanel(new GridBagLayout()); + keyPanel.setBorder(BorderFactory.createTitledBorder("Key")); + + // Create the main panel + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.NONE; + gbc.anchor = GridBagConstraints.WEST; + + Insets normalInsets = new Insets(0, 2, 4, 2); + Insets indentedInsets = new Insets(0, 26, 4, 2); + gbc.insets = normalInsets; + gbc.weightx = 0.0; + + // Action + UIUtil.jGridBagAdd(keyPanel, new JLabel("Action"), gbc, 1); + gbc.weightx = 1.0; + action = new JComboBox(new String[] { + "Generate key pair", "Convert IETF SECSH to OpenSSH", + "Convert OpenSSH to IETF SECSH", "Change passphrase" + }); + action.addActionListener(this); + gbc.weightx = 2.0; + UIUtil.jGridBagAdd(keyPanel, action, gbc, GridBagConstraints.RELATIVE); + gbc.weightx = 0.0; + UIUtil.jGridBagAdd(keyPanel, new JLabel(), gbc, + GridBagConstraints.REMAINDER); + gbc.insets = indentedInsets; + + // File + inputFileLabel = new JLabel("Input File"); + UIUtil.jGridBagAdd(keyPanel, inputFileLabel, gbc, 1); + gbc.insets = normalInsets; + gbc.weightx = 1.0; + inputFile = new XTextField(20); + UIUtil.jGridBagAdd(keyPanel, inputFile, gbc, GridBagConstraints.RELATIVE); + inputFileLabel.setLabelFor(inputFile); + gbc.weightx = 0.0; + browseInput = new JButton("Browse"); + browseInput.setMnemonic('b'); + browseInput.addActionListener(this); + UIUtil.jGridBagAdd(keyPanel, browseInput, gbc, + GridBagConstraints.REMAINDER); + + // File + gbc.insets = indentedInsets; + outputFileLabel = new JLabel("Output File"); + UIUtil.jGridBagAdd(keyPanel, outputFileLabel, gbc, 1); + gbc.insets = normalInsets; + gbc.weightx = 1.0; + outputFile = new XTextField(20); + UIUtil.jGridBagAdd(keyPanel, outputFile, gbc, + GridBagConstraints.RELATIVE); + gbc.weightx = 0.0; + outputFileLabel.setLabelFor(outputFile); + browseOutput = new JButton("Browse"); + browseOutput.setMnemonic('r'); + browseOutput.addActionListener(this); + UIUtil.jGridBagAdd(keyPanel, browseOutput, gbc, + GridBagConstraints.REMAINDER); + + // Old Passphrase + gbc.insets = indentedInsets; + oldPassphraseLabel = new JLabel("Old Passphrase"); + UIUtil.jGridBagAdd(keyPanel, oldPassphraseLabel, gbc, 1); + gbc.insets = normalInsets; + gbc.weightx = 2.0; + oldPassphrase = new JPasswordField(20); + oldPassphrase.setBackground(Color.white); + oldPassphrase.getDocument().addDocumentListener(this); + oldPassphraseLabel.setLabelFor(oldPassphrase); + UIUtil.jGridBagAdd(keyPanel, oldPassphrase, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(keyPanel, new JLabel(), gbc, + GridBagConstraints.REMAINDER); + + // Passphrase + gbc.insets = indentedInsets; + newPassphraseLabel = new JLabel("New Passphrase"); + UIUtil.jGridBagAdd(keyPanel, newPassphraseLabel, gbc, 1); + gbc.insets = normalInsets; + gbc.weightx = 2.0; + newPassphrase = new JPasswordField(20); + newPassphrase.setBackground(Color.white); + newPassphrase.getDocument().addDocumentListener(this); + newPassphraseLabel.setLabelFor(newPassphrase); + UIUtil.jGridBagAdd(keyPanel, newPassphrase, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(keyPanel, new JLabel(), gbc, + GridBagConstraints.REMAINDER); + + // Bits + gbc.insets = indentedInsets; + bitsLabel = new JLabel("Bits"); + UIUtil.jGridBagAdd(keyPanel, bitsLabel, gbc, 1); + gbc.weightx = 2.0; + gbc.insets = normalInsets; + bits = new NumericTextField(new Integer(512), new Integer(1024), + new Integer(1024)); + bitsLabel.setLabelFor(bits); + UIUtil.jGridBagAdd(keyPanel, bits, gbc, GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(keyPanel, new JLabel(), gbc, + GridBagConstraints.REMAINDER); + + // Type + gbc.insets = indentedInsets; + typeLabel = new JLabel("Type"); + UIUtil.jGridBagAdd(keyPanel, typeLabel, gbc, 1); + gbc.insets = normalInsets; + gbc.weightx = 2.0; + type = new JComboBox(new String[] { "DSA", "RSA" }); + type.setFont(inputFile.getFont()); + + // Combo boxes look crap in metal + UIUtil.jGridBagAdd(keyPanel, type, gbc, GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(keyPanel, new JLabel(), gbc, + GridBagConstraints.REMAINDER); + strength = new JProgressBar(0, 40); + strength.setStringPainted(true); + + JPanel strengthPanel = new JPanel(new GridLayout(1, 1)); + strengthPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createTitledBorder("Passphrase strength"), + BorderFactory.createEmptyBorder(4, 4, 4, 4))); + strengthPanel.add(strength); + + // Build this panel + setLayout(new BorderLayout()); + add(keyPanel, BorderLayout.CENTER); + add(strengthPanel, BorderLayout.SOUTH); + calculateStrength(); + setAvailableActions(); + } + + /** +* +* +* @return +*/ + public int getAction() { + return action.getSelectedIndex(); + } + + /** +* +* +* @return +*/ + public int getBits() { + return ((Integer) bits.getValue()).intValue(); + } + + /** +* +* +* @return +*/ + public String getInputFilename() { + return inputFile.getText(); + } + + /** +* +* +* @return +*/ + public char[] getNewPassphrase() { + return newPassphrase.getPassword(); + } + + /** +* +* +* @return +*/ + public char[] getOldPassphrase() { + return oldPassphrase.getPassword(); + } + + /** +* +* +* @return +*/ + public String getOutputFilename() { + return outputFile.getText(); + } + + /** +* +* +* @return +*/ + public String getType() { + return (String) type.getSelectedItem(); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == browseOutput) { + File f = new File(outputFile.getText()); + JFileChooser chooser = new JFileChooser(f); + chooser.setSelectedFile(f); + chooser.setDialogTitle("Choose output file .."); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { + outputFile.setText(chooser.getSelectedFile().getPath()); + } + } else if (evt.getSource() == browseInput) { + File f = new File(inputFile.getText()); + JFileChooser chooser = new JFileChooser(f); + chooser.setSelectedFile(f); + chooser.setDialogTitle("Choose input file .."); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + inputFile.setText(chooser.getSelectedFile().getPath()); + } + } else { + setAvailableActions(); + } + } + + /** +* +* +* @param e +*/ + public void changedUpdate(DocumentEvent e) { + calculateStrength(); + } + + /** +* +* +* @param e +*/ + public void insertUpdate(DocumentEvent e) { + calculateStrength(); + } + + /** +* +* +* @param e +*/ + public void removeUpdate(DocumentEvent e) { + calculateStrength(); + } + + private void setAvailableActions() { + inputFile.setEnabled((getAction() == CONVERT_IETF_SECSH_TO_OPENSSH) || + (getAction() == CONVERT_OPENSSH_TO_IETF_SECSH) || + (getAction() == CHANGE_PASSPHRASE)); + inputFileLabel.setEnabled(inputFile.isEnabled()); + browseInput.setEnabled(inputFile.isEnabled()); + bits.setEnabled(getAction() == GENERATE_KEY_PAIR); + bitsLabel.setEnabled(bits.isEnabled()); + outputFile.setEnabled((getAction() == CONVERT_IETF_SECSH_TO_OPENSSH) || + (getAction() == CONVERT_OPENSSH_TO_IETF_SECSH) || + (getAction() == GENERATE_KEY_PAIR)); + outputFileLabel.setEnabled(outputFile.isEnabled()); + browseOutput.setEnabled(outputFile.isEnabled()); + newPassphrase.setEnabled((getAction() == GENERATE_KEY_PAIR) || + (getAction() == CHANGE_PASSPHRASE)); + newPassphraseLabel.setEnabled(newPassphrase.isEnabled()); + oldPassphrase.setEnabled(getAction() == CHANGE_PASSPHRASE); + oldPassphraseLabel.setEnabled(oldPassphrase.isEnabled()); + type.setEnabled(getAction() == GENERATE_KEY_PAIR); + typeLabel.setEnabled(type.isEnabled()); + + if (inputFile.isEnabled()) { + inputFile.requestFocus(); + } else { + outputFile.requestFocus(); + } + } + + private void calculateStrength() { + char[] pw = newPassphrase.getPassword(); + strength.setValue((pw.length < 40) ? pw.length : 40); + + Color f; + String t; + + if (pw.length == 0) { + f = Color.red; + t = "Empty!!"; + } else { + StringBuffer buf = new StringBuffer(); + buf.append(pw.length); + buf.append(" characters - "); + + if (pw.length < 10) { + f = Color.red; + buf.append("Weak!"); + } else if (pw.length < 20) { + f = Color.orange; + buf.append("Ok"); + } else if (pw.length < 30) { + f = Color.green.darker(); + buf.append("Strong"); + } else { + f = Color.green; + buf.append("Very strong!"); + } + + t = buf.toString(); + } + + strength.setString(t); + strength.setForeground(f); + } +} diff --git a/src/com/sshtools/common/keygen/KeygenPanel2.java b/src/com/sshtools/common/keygen/KeygenPanel2.java new file mode 100644 index 0000000000000000000000000000000000000000..211c446b8798f007c508364c2a7ec315f497c40e --- /dev/null +++ b/src/com/sshtools/common/keygen/KeygenPanel2.java @@ -0,0 +1,126 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.keygen; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.border.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class KeygenPanel2 extends JPanel { + // Actions + + /** */ + public final static int GENERATE_KEY_PAIR = 0; + + /** */ + public final static int CONVERT_IETF_SECSH_TO_OPENSSH = 1; + + /** */ + public final static int CONVERT_OPENSSH_TO_IETF_SECSH = 2; + + /** */ + public final static int CHANGE_PASSPHRASE = 3; + + // Private instance variables + private JButton browseInput; + BorderLayout borderLayout1 = new BorderLayout(); + JPanel jPanel1 = new JPanel(); + JTabbedPane key1Panel = new JTabbedPane(); + JPanel key1GeneratePanel = new JPanel(); + JPanel file = new JPanel(); + BorderLayout borderLayout2 = new BorderLayout(); + JPanel key1DataPanel = new JPanel(); + JScrollPane jScrollPane1 = new JScrollPane(); + JTextArea jTextArea1 = new JTextArea(); + GridLayout gridLayout1 = new GridLayout(); + TitledBorder titledBorder1; + TitledBorder titledBorder2; + GridBagLayout gridBagLayout1 = new GridBagLayout(); + JLabel jLabel1 = new JLabel(); + JTextField jTextField1 = new JTextField(); + JButton jButton1 = new JButton(); + JButton jButton2 = new JButton(); + + /** +* Creates a new KeygenPanel2 object. +*/ + public KeygenPanel2() { + super(); + + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void jbInit() throws Exception { + titledBorder1 = new TitledBorder("Data"); + titledBorder2 = new TitledBorder("Key 1"); + this.setLayout(borderLayout1); + jPanel1.setLayout(borderLayout2); + jTextArea1.setText("jTextArea1"); + key1DataPanel.setLayout(gridLayout1); + key1DataPanel.setBorder(titledBorder1); + jPanel1.setBorder(titledBorder2); + file.setLayout(gridBagLayout1); + jLabel1.setText("File:"); + jTextField1.setText("jTextField1"); + jButton1.setText("Open"); + jButton2.setText("Browse"); + this.add(jPanel1, BorderLayout.CENTER); + jPanel1.add(key1Panel, BorderLayout.NORTH); + key1Panel.add(key1GeneratePanel, "Generate"); + key1Panel.add(file, "File"); + jPanel1.add(key1DataPanel, BorderLayout.CENTER); + key1DataPanel.add(jScrollPane1, null); + jScrollPane1.getViewport().add(jTextArea1, null); + file.add(jLabel1, + new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(2, 2, 0, 3), 0, 0)); + file.add(jTextField1, + new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(2, 2, 0, 0), 0, 0)); + file.add(jButton1, + new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(2, 2, 0, 2), 0, 0)); + file.add(jButton2, + new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(2, 0, 0, 2), 0, 0)); + } +} diff --git a/src/com/sshtools/common/keygen/Main.java b/src/com/sshtools/common/keygen/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..d887128d1f0266ecec5bef7e98dff88227c024d8 --- /dev/null +++ b/src/com/sshtools/common/keygen/Main.java @@ -0,0 +1,321 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.keygen; + +import com.sshtools.common.ui.IconWrapperPanel; +import com.sshtools.common.ui.ResourceIcon; +import com.sshtools.common.ui.UIUtil; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.transport.publickey.OpenSSHPublicKeyFormat; +import com.sshtools.j2ssh.transport.publickey.SECSHPublicKeyFormat; +import com.sshtools.j2ssh.transport.publickey.SshKeyGenerator; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.ProgressMonitor; +import javax.swing.SwingConstants; +import javax.swing.UIManager; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class Main extends JFrame implements ActionListener { + // Statics + final static String ICON = "/com/sshtools/common/authentication/largepassphrase.png"; + JButton close; + JButton generate; + KeygenPanel keygen; + + /** +* Creates a new Main object. +*/ + public Main() { + super("ssh-keygen"); + + /* try { +ConfigurationLoader.setLogfile(ConfigurationLoader.getHomeDirectory() + + "logs/ssh-keygen.log"); +} catch (IOException ex) { +}*/ + try { + ConfigurationLoader.initialize(false); + } catch (ConfigurationException ex) { + } + + // Set the frame icon + setIconImage(new ResourceIcon(ICON).getImage()); + + // + keygen = new KeygenPanel(); + + // Create the center banner panel + IconWrapperPanel centerPanel = new IconWrapperPanel(new ResourceIcon( + ICON), keygen); + centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // Button panel + JPanel buttonPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(6, 6, 0, 0); + gbc.weighty = 1.0; + generate = new JButton("Generate"); + generate.addActionListener(this); + generate.setMnemonic('g'); + this.getRootPane().setDefaultButton(generate); + UIUtil.jGridBagAdd(buttonPanel, generate, gbc, + GridBagConstraints.RELATIVE); + close = new JButton("Close"); + close.addActionListener(this); + close.setMnemonic('c'); + UIUtil.jGridBagAdd(buttonPanel, close, gbc, GridBagConstraints.REMAINDER); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + southPanel.add(buttonPanel); + + // Wrap the whole thing in an empty border + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(centerPanel, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + + // Build the main panel + getContentPane().setLayout(new GridLayout(1, 1)); + getContentPane().add(mainPanel); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + // Close + if (evt.getSource() == close) { + dispose(); + + return; + } + + final String newPassphrase = new String(keygen.getNewPassphrase()).trim(); + final String oldPassphrase = new String(keygen.getOldPassphrase()).trim(); + + if ((keygen.getAction() == KeygenPanel.GENERATE_KEY_PAIR) || + (keygen.getAction() == KeygenPanel.CHANGE_PASSPHRASE)) { + if (newPassphrase.length() == 0) { + if (JOptionPane.showConfirmDialog(this, + "Passphrase is empty. Are you sure?", + "Empty Passphrase", JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) { + return; + } + } + } + + final File inputFile = new File(keygen.getInputFilename()); + final File outputFile = new File(keygen.getOutputFilename()); + final File publicFile = new File(keygen.getOutputFilename() + ".pub"); + + // Check if the output file was supplied + if ((keygen.getAction() == KeygenPanel.CONVERT_IETF_SECSH_TO_OPENSSH) || + (keygen.getAction() == KeygenPanel.CONVERT_OPENSSH_TO_IETF_SECSH) || + (keygen.getAction() == KeygenPanel.GENERATE_KEY_PAIR)) { + if (keygen.getOutputFilename().length() == 0) { + JOptionPane.showMessageDialog(this, "No Output file supplied.", + "Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + // Check if the output file exists, and confirm overwrit if it does + if (outputFile.exists()) { + if (JOptionPane.showConfirmDialog(this, + "Output file " + outputFile.getName() + + " exists. Are you sure?", "File exists", + JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) { + return; + } + } + + // Make sure the output file is writeable + if (outputFile.exists() && !outputFile.canWrite()) { + JOptionPane.showMessageDialog(this, + "Output file " + outputFile.getName() + + " can not be written.", "Unwriteable file", + JOptionPane.ERROR_MESSAGE); + + return; + } + } + + // If this is a conversion, check the input file is provided + if ((keygen.getAction() == KeygenPanel.CONVERT_IETF_SECSH_TO_OPENSSH) || + (keygen.getAction() == KeygenPanel.CONVERT_OPENSSH_TO_IETF_SECSH)) { + if (keygen.getInputFilename().length() == 0) { + JOptionPane.showMessageDialog(this, "No Input file supplied.", + "Error", JOptionPane.ERROR_MESSAGE); + + return; + } + } else if (keygen.getAction() == KeygenPanel.GENERATE_KEY_PAIR) { + // Check if the public key file is writeable. We should test if it exists + // as thats just too many questions for the user + if (publicFile.exists() && !publicFile.canWrite()) { + JOptionPane.showMessageDialog(this, + "Public key file " + publicFile.getName() + + " can not be written.", "Unwriteable file", + JOptionPane.ERROR_MESSAGE); + + return; + } + } + + // Now generate the key + final ProgressMonitor monitor = new ProgressMonitor(this, + "Generating keys", "Generating", 0, 100); + monitor.setMillisToDecideToPopup(0); + monitor.setMillisToPopup(0); + + Runnable r = new Runnable() { + public void run() { + try { + if (keygen.getAction() == KeygenPanel.CHANGE_PASSPHRASE) { + monitor.setNote("Changing passphrase"); + SshKeyGenerator.changePassphrase(inputFile, + oldPassphrase, newPassphrase); + monitor.setNote("Complete"); + JOptionPane.showMessageDialog(Main.this, + "Passphrase changed", "Passphrase changed", + JOptionPane.INFORMATION_MESSAGE); + } else if (keygen.getAction() == KeygenPanel.CONVERT_IETF_SECSH_TO_OPENSSH) { + monitor.setNote("Converting key file"); + writeString(outputFile, + SshKeyGenerator.convertPublicKeyFile( + inputFile, new OpenSSHPublicKeyFormat())); + monitor.setNote("Complete"); + JOptionPane.showMessageDialog(Main.this, + "Key converted", "Key converted", + JOptionPane.INFORMATION_MESSAGE); + } else if (keygen.getAction() == KeygenPanel.CONVERT_OPENSSH_TO_IETF_SECSH) { + monitor.setNote("Converting key file"); + writeString(outputFile, + SshKeyGenerator.convertPublicKeyFile( + inputFile, new SECSHPublicKeyFormat())); + monitor.setNote("Complete"); + JOptionPane.showMessageDialog(Main.this, + "Key converted", "Key converted", + JOptionPane.INFORMATION_MESSAGE); + } else { + monitor.setNote("Creating generator"); + + SshKeyGenerator generator = new SshKeyGenerator(); + monitor.setNote("Generating"); + + String username = System.getProperty("user.name"); + generator.generateKeyPair(keygen.getType(), + keygen.getBits(), outputFile.getAbsolutePath(), + username, newPassphrase); + monitor.setNote("Complete"); + JOptionPane.showMessageDialog(Main.this, + "Key generated to " + outputFile.getName(), + "Complete", JOptionPane.INFORMATION_MESSAGE); + } + } catch (Exception e) { + JOptionPane.showMessageDialog(Main.this, + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } finally { + monitor.close(); + } + } + }; + + Thread t = new Thread(r); + t.start(); + } + + /** +* +* +* @param args +*/ + public static void main(String[] args) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + } + + Main main = new Main(); + main.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + System.exit(0); + } + }); + main.pack(); + UIUtil.positionComponent(SwingConstants.CENTER, main); + main.setVisible(true); + } + + private void writeString(File file, String string) + throws IOException { + FileOutputStream out = null; + + try { + out = new FileOutputStream(file); + + PrintWriter w = new PrintWriter(out, true); + w.println(string); + } finally { + if (out != null) { + out.close(); + } + } + } +} diff --git a/src/com/sshtools/common/keygen/keygen.png b/src/com/sshtools/common/keygen/keygen.png new file mode 100644 index 0000000000000000000000000000000000000000..6dd431445c70e0d3c0e907fb8997df79e0cb1fdc Binary files /dev/null and b/src/com/sshtools/common/keygen/keygen.png differ diff --git a/src/com/sshtools/common/keygen/largekeygen.png b/src/com/sshtools/common/keygen/largekeygen.png new file mode 100644 index 0000000000000000000000000000000000000000..e1a6f844ba2c530136d9167b5e594e3112d56ec2 Binary files /dev/null and b/src/com/sshtools/common/keygen/largekeygen.png differ diff --git a/src/com/sshtools/common/mru/MRUAction.java b/src/com/sshtools/common/mru/MRUAction.java new file mode 100644 index 0000000000000000000000000000000000000000..bb325a421dbffe37a1e24796b385749a3a4fb9d5 --- /dev/null +++ b/src/com/sshtools/common/mru/MRUAction.java @@ -0,0 +1,63 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.mru; + +import com.sshtools.common.ui.EmptyIcon; +import com.sshtools.common.ui.MenuAction; +import com.sshtools.common.ui.StandardAction; + +import javax.swing.Action; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public abstract class MRUAction extends MenuAction { + /** +* Creates a new MRUAction object. +* +* @param model +*/ + public MRUAction(MRUListModel model) { + putValue(Action.NAME, "Recent"); + putValue(Action.SMALL_ICON, new EmptyIcon(16, 16)); + putValue(Action.SHORT_DESCRIPTION, "Recent connections"); + putValue(Action.LONG_DESCRIPTION, "Recent connection files"); + putValue(Action.MNEMONIC_KEY, new Integer('r')); + putValue(Action.ACTION_COMMAND_KEY, "recent"); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(99)); + + MRUMenu menu = new MRUMenu(this, model); + menu.addActionListener(this); + putValue(MenuAction.MENU, new MRUMenu(this, model)); + } +} diff --git a/src/com/sshtools/common/mru/MRUList.java b/src/com/sshtools/common/mru/MRUList.java new file mode 100644 index 0000000000000000000000000000000000000000..7a20a12f6059c637383eb61102ec210b2fc261d4 --- /dev/null +++ b/src/com/sshtools/common/mru/MRUList.java @@ -0,0 +1,196 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.mru; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import java.util.Iterator; +import java.util.Stack; +import java.util.Vector; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class MRUList extends java.util.Vector { + private static Log log = LogFactory.getLog(MRUList.class); + private static final String MRU_LIST_ELEMENT = "MRUList"; + private static final String FILE_ELEMENT = "File"; + private String currentElement = null; + + /** +* Creates a new MRUList object. +*/ + public MRUList() { + super(); + } + + /** +* Creates a new MRUList object. +* +* @param in +* +* @throws SAXException +* @throws ParserConfigurationException +* @throws IOException +*/ + public MRUList(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + this(); + reload(in); + } + + /** +* +* +* @param in +* +* @throws SAXException +* @throws ParserConfigurationException +* @throws IOException +*/ + public void reload(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + saxParser.parse(in, new MRUSAXHandler()); + } + + /** +* +* +* @return +*/ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += ("<!-- Most recently used -->\n<" + MRU_LIST_ELEMENT + ">\n"); + + Iterator it = iterator(); + File file = null; + + while (it.hasNext()) { + file = (File) it.next(); + xml += (" " + "<" + FILE_ELEMENT + ">" + file.getAbsolutePath() + + "</" + FILE_ELEMENT + ">\n"); + } + + xml += ("</" + MRU_LIST_ELEMENT + ">"); + + return xml; + } + + private class MRUSAXHandler extends DefaultHandler { + private String MRU_LIST_ELEMENT = "MRUList"; + private String FILE_ELEMENT = "File"; + private File currentFile = null; + private Stack tags = new Stack(); + + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + ElementWrapper currentElement = (tags.size() == 0) ? null + : (ElementWrapper) tags.peek(); + + if (currentElement == null) { + if (!qname.equals(MRU_LIST_ELEMENT)) { + throw new SAXException("Unexpected root element <" + qname + + ">"); + } + } else { + if (currentElement.element.equals(MRU_LIST_ELEMENT)) { + if (qname.equals(FILE_ELEMENT)) { + } else { + throw new SAXException("Unexpected element <" + qname + + ">"); + } + } else { + throw new SAXException("Unexpected element <" + qname + + ">"); + } + } + + ElementWrapper w = new ElementWrapper(qname); + tags.push(w); + } + + public void characters(char[] ch, int start, int len) + throws SAXException { + ElementWrapper currentElement = (tags.size() == 0) ? null + : (ElementWrapper) tags.peek(); + + if (currentElement != null) { + currentElement.text.append(new String(ch, start, len)); + } else { + throw new SAXException("Unexpected text at " + start + " for " + + len); + } + } + + public void endElement(String uri, String localName, String qname) + throws SAXException { + ElementWrapper currentElement = (tags.size() == 0) ? null + : (ElementWrapper) tags.peek(); + + if (currentElement != null) { + if (!currentElement.element.equals(qname)) { + throw new SAXException("Unexpected end element found <" + + qname + ">"); + } + + if (currentElement.element.equals(FILE_ELEMENT)) { + MRUList.this.add(new File(currentElement.text.toString())); + } + + tags.pop(); + } + } + } + + public class ElementWrapper { + String element; + StringBuffer text; + + ElementWrapper(String element) { + this.element = element; + text = new StringBuffer(); + } + } +} diff --git a/src/com/sshtools/common/mru/MRUListModel.java b/src/com/sshtools/common/mru/MRUListModel.java new file mode 100644 index 0000000000000000000000000000000000000000..ae2656bdf2aa38109f4a54b8c3a87fd595e4fd17 --- /dev/null +++ b/src/com/sshtools/common/mru/MRUListModel.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.mru; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; + +import javax.swing.AbstractListModel; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class MRUListModel extends AbstractListModel { + private static Log log = LogFactory.getLog(MRUListModel.class); + private MRUList mru; + + /** +* Creates a new MRUListModel object. +*/ + public MRUListModel() { + super(); + setMRUList(new MRUList()); + } + + /** +* +* +* @param f +*/ + public void add(File f) { + mru.insertElementAt(f, 0); + + for (int i = mru.size() - 1; i >= 1; i--) { + if (((File) mru.elementAt(i)).equals(f)) { + mru.removeElementAt(i); + } + } + + if (mru.size() > 15) { + for (int i = mru.size() - 1; i >= 15; i--) { + mru.removeElementAt(i); + } + } + + fireContentsChanged(this, 0, getSize() - 1); + } + + /** +* +* +* @param i +* +* @return +*/ + public Object getElementAt(int i) { + return mru.get(i); + } + + /** +* +* +* @return +*/ + public int getSize() { + return (mru == null) ? 0 : mru.size(); + } + + /** +* +* +* @param mru +*/ + public void setMRUList(MRUList mru) { + this.mru = mru; + fireContentsChanged(this, 0, getSize()); + } + + /** +* +* +* @return +*/ + public MRUList getMRUList() { + return mru; + } +} diff --git a/src/com/sshtools/common/mru/MRUMenu.java b/src/com/sshtools/common/mru/MRUMenu.java new file mode 100644 index 0000000000000000000000000000000000000000..addfc68c1dcb03b867921f1fcec77d823c3b9496 --- /dev/null +++ b/src/com/sshtools/common/mru/MRUMenu.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.mru; + +import java.awt.*; +import java.awt.event.*; + +import java.io.*; + +import javax.swing.*; +import javax.swing.event.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class MRUMenu extends JMenu implements ListDataListener, ActionListener { + private MRUListModel model; + + /** +* Creates a new MRUMenu object. +* +* @param action +* @param model +*/ + protected MRUMenu(Action action, MRUListModel model) { + super(action); + init(model); + } + + /** +* Creates a new MRUMenu object. +* +* @param text +* @param model +*/ + protected MRUMenu(String text, MRUListModel model) { + super(text); + init(model); + } + + private void init(MRUListModel model) { + this.model = model; + rebuildMenu(); + model.addListDataListener(this); + } + + /** +* +*/ + public void cleanUp() { + model.removeListDataListener(this); + } + + /** +* +* +* @param e +*/ + public void intervalAdded(ListDataEvent e) { + rebuildMenu(); + } + + /** +* +* +* @param e +*/ + public void intervalRemoved(ListDataEvent e) { + rebuildMenu(); + } + + /** +* +* +* @param e +*/ + public void contentsChanged(ListDataEvent e) { + rebuildMenu(); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + fireActionPerformed(evt); + } + + private void rebuildMenu() { + Component[] c = getMenuComponents(); + + for (int i = 0; (c != null) && (i < c.length); i++) { + ((JMenuItem) c[i]).removeActionListener(this); + remove(c[i]); + } + + for (int i = 0; i < model.getSize(); i++) { + File f = (File) model.getElementAt(i); + JMenuItem m = new JMenuItem(f.getName()); + m.setActionCommand(f.getAbsolutePath()); + m.setToolTipText(f.getAbsolutePath()); + m.addActionListener(this); + add(m); + } + + setEnabled(model.getSize() > 0); + validate(); + } +} diff --git a/src/com/sshtools/common/ui/AboutAction.java b/src/com/sshtools/common/ui/AboutAction.java new file mode 100644 index 0000000000000000000000000000000000000000..3205c5128e70b59a737655aece8fc5ee61eb9b76 --- /dev/null +++ b/src/com/sshtools/common/ui/AboutAction.java @@ -0,0 +1,84 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.Action; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class AboutAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_ABOUT = "about-command"; + private final static String NAME_ABOUT = "About"; + private final static String SMALL_ICON_ABOUT = "/com/sshtools/common/ui/about.png"; + private final static String LARGE_ICON_ABOUT = ""; + private final static int MNEMONIC_KEY_ABOUT = 'A'; + private SshToolsApplication application; + private Component parent; + + /** +* Creates a new AboutAction object. +* +* @param parent +* @param application +*/ + public AboutAction(Component parent, SshToolsApplication application) { + this.application = application; + this.parent = parent; + putValue(Action.NAME, NAME_ABOUT); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_ABOUT)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_ABOUT)); + putValue(Action.SHORT_DESCRIPTION, + "About " + application.getApplicationName()); + putValue(Action.LONG_DESCRIPTION, + "Show information about " + application.getApplicationName()); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_ABOUT)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_ABOUT); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "Help"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(90)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(90)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(90)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(10)); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + application.showAbout(parent); + } +} diff --git a/src/com/sshtools/common/ui/BooleanIconRenderer.java b/src/com/sshtools/common/ui/BooleanIconRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..bcf8c76818b96286a0d5db0174c11d001d49dcf3 --- /dev/null +++ b/src/com/sshtools/common/ui/BooleanIconRenderer.java @@ -0,0 +1,60 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.table.*; + + +public class BooleanIconRenderer extends DefaultTableCellRenderer { + // Private instance variables + private Icon trueIcon; + + // Private instance variables + private Icon falseIcon; + + public BooleanIconRenderer(Icon trueIcon, Icon falseIcon) { + this.trueIcon = trueIcon; + this.falseIcon = falseIcon; + setHorizontalAlignment(JLabel.CENTER); + } + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + setText(null); + setIcon(((Boolean) value).booleanValue() ? trueIcon : falseIcon); + + return this; + } + + public String getText() { + return null; + } +} diff --git a/src/com/sshtools/common/ui/CloseAction.java b/src/com/sshtools/common/ui/CloseAction.java new file mode 100644 index 0000000000000000000000000000000000000000..cca95ec10fead275e87bd352e43d6b1b41faf6e9 --- /dev/null +++ b/src/com/sshtools/common/ui/CloseAction.java @@ -0,0 +1,70 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class CloseAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_CLOSE = "close-command"; + private final static String NAME_CLOSE = "Close"; + private final static String SMALL_ICON_CLOSE = "/com/sshtools/common/ui/close.png"; + private final static String LARGE_ICON_CLOSE = ""; + private final static String SHORT_DESCRIPTION_CLOSE = "Close the current connection"; + private final static String LONG_DESCRIPTION_CLOSE = "Close the current connection"; + private final static int MNEMONIC_KEY_CLOSE = 'C'; + + /** +* Creates a new CloseAction object. +*/ + public CloseAction() { + putValue(Action.NAME, NAME_CLOSE); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_CLOSE)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_CLOSE)); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.ALT_MASK)); + putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION_CLOSE); + putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION_CLOSE); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_CLOSE)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_CLOSE); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(60)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(0)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(10)); + } +} diff --git a/src/com/sshtools/common/ui/ColorComboBox.java b/src/com/sshtools/common/ui/ColorComboBox.java new file mode 100644 index 0000000000000000000000000000000000000000..d93f0f7acdc18b4ca106909d54c72fac7c0be083 --- /dev/null +++ b/src/com/sshtools/common/ui/ColorComboBox.java @@ -0,0 +1,278 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.util.Vector; + +import javax.swing.AbstractListModel; +import javax.swing.BorderFactory; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ColorComboBox extends JComboBox { + /** +* Creates a new ColorComboBox object. +*/ + public ColorComboBox() { + this(null); + } + + /** +* Creates a new ColorComboBox object. +* +* @param color +*/ + public ColorComboBox(Color color) { + super(new ColorComboModel()); + setColor(color); + setRenderer(new ColorRenderer()); + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (getSelectedItem() == null) { + chooseCustomColor(); + } else { + fireChangeEvent(); + } + } + }); + } + + /** +* +*/ + protected void fireChangeEvent() { + ChangeEvent evt = new ChangeEvent(this); + ChangeListener[] l = (ChangeListener[]) listenerList.getListeners(ChangeListener.class); + + for (int i = (l.length - 1); i >= 0; i--) { + l[i].stateChanged(evt); + } + } + + /** +* +* +* @param l +*/ + public void addChangeListener(ChangeListener l) { + listenerList.add(ChangeListener.class, l); + } + + /** +* +* +* @param l +*/ + public void removeChangeListener(ChangeListener l) { + listenerList.remove(ChangeListener.class, l); + } + + private void chooseCustomColor() { + Color c = JColorChooser.showDialog(this, "Custom Color", Color.black); + + if (c != null) { + setColor(c); + fireChangeEvent(); + } + } + + /** +* +* +* @param c +*/ + public void setColor(Color c) { + for (int i = 0; i < (getModel().getSize() - 1); i++) { + Color z = (Color) getModel().getElementAt(i); + + if (z.equals(c)) { + setSelectedIndex(i); + + return; + } + } + + if (c != null) { + ((ColorComboModel) getModel()).addColor(c); + } + } + + /** +* +* +* @return +*/ + public Color getColor() { + return (Color) getSelectedItem(); + } + + // Supporting classes + static class ColorComboModel extends AbstractListModel + implements ComboBoxModel { + private Vector colors = new Vector(); + private Object selected; + + ColorComboModel() { + colors = new Vector(); + + // Add the initial colors + colors.addElement(Color.black); + colors.addElement(Color.white); + colors.addElement(Color.red.darker()); + colors.addElement(Color.red); + colors.addElement(Color.orange.darker()); + colors.addElement(Color.orange); + colors.addElement(Color.yellow.darker()); + colors.addElement(Color.yellow); + colors.addElement(Color.green.darker()); + colors.addElement(Color.green); + colors.addElement(Color.blue.darker()); + colors.addElement(Color.blue); + colors.addElement(Color.cyan.darker()); + colors.addElement(Color.cyan); + colors.addElement(Color.magenta.darker()); + colors.addElement(Color.magenta); + colors.addElement(Color.pink.darker()); + colors.addElement(Color.pink); + colors.addElement(Color.lightGray); + colors.addElement(Color.gray); + colors.addElement(Color.darkGray); + + // Black is initialy selected + selected = colors.elementAt(0); + } + + public int getSize() { + return colors.size() + 1; + } + + public Object getElementAt(int i) { + if (i == colors.size()) { + return null; + } else { + return colors.elementAt(i); + } + } + + public void setSelectedItem(Object sel) { + selected = sel; + } + + public Object getSelectedItem() { + return selected; + } + + public void addColor(Color c) { + int idx = colors.size(); + colors.addElement(c); + selected = c; + fireIntervalAdded(this, idx, idx); + } + } + + class ColorRenderer extends DefaultListCellRenderer { + private ColorIcon icon; + + ColorRenderer() { + icon = new ColorIcon(Color.black, new Dimension(10, 10), Color.black); + setBorder(BorderFactory.createEmptyBorder(0, 16, 0, 0)); + } + + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); + + Color c = (Color) value; + + // If the value is null. Then this signifies custom color + if (c == null) { + setIcon(javax.swing.plaf.basic.BasicIconFactory.getCheckBoxIcon()); + setText("Custom ...."); + } else { + // Set up the icon + icon.setColor(c); + setIcon(icon); + + // Set the text. If the color is a well known one with a name, render + // the name. Otherwise use the RGB values + String s = "#" + c.getRed() + "," + c.getGreen() + "," + + c.getBlue(); + + if (c.equals(Color.black)) { + s = "Black"; + } else if (c.equals(Color.white)) { + s = "White"; + } else if (c.equals(Color.red)) { + s = "Red"; + } else if (c.equals(Color.orange)) { + s = "Orange"; + } else if (c.equals(Color.yellow)) { + s = "Yellow"; + } else if (c.equals(Color.green)) { + s = "Green"; + } else if (c.equals(Color.blue)) { + s = "Blue"; + } else if (c.equals(Color.cyan)) { + s = "Cyan"; + } else if (c.equals(Color.magenta)) { + s = "Magenta"; + } else if (c.equals(Color.pink)) { + s = "Pink"; + } else if (c.equals(Color.lightGray)) { + s = "Light Gray"; + } else if (c.equals(Color.gray)) { + s = "Gray"; + } else if (c.equals(Color.darkGray)) { + s = "Dark Gray"; + } + + setText(s); + } + + // + return this; + } + } +} diff --git a/src/com/sshtools/common/ui/ColorIcon.java b/src/com/sshtools/common/ui/ColorIcon.java new file mode 100644 index 0000000000000000000000000000000000000000..d8c5d726032b23062784ca92ba194c301873be4d --- /dev/null +++ b/src/com/sshtools/common/ui/ColorIcon.java @@ -0,0 +1,146 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ColorIcon implements Icon { + // Private instance variables + private Dimension size; + private Color color; + private Color borderColor; + + /** +* Creates a new ColorIcon object. +*/ + public ColorIcon() { + this(null); + } + + /** +* Creates a new ColorIcon object. +* +* @param color +*/ + public ColorIcon(Color color) { + this(color, null); + } + + /** +* Creates a new ColorIcon object. +* +* @param color +* @param borderColor +*/ + public ColorIcon(Color color, Color borderColor) { + this(color, null, borderColor); + } + + /** +* Creates a new ColorIcon object. +* +* @param color +* @param size +* @param borderColor +*/ + public ColorIcon(Color color, Dimension size, Color borderColor) { + setColor(color); + setSize(size); + setBorderColor(borderColor); + } + + /** +* +* +* @param c +* @param g +* @param x +* @param y +*/ + public void paintIcon(Component c, Graphics g, int x, int y) { + g.setColor((color == null) ? Color.black : color); + g.fillRect(x, y, getIconWidth(), getIconHeight()); + + if (borderColor != null) { + g.setColor(borderColor); + g.drawRect(x, y, getIconWidth(), getIconHeight()); + } + } + + /** +* +* +* @param size +*/ + public void setSize(Dimension size) { + this.size = size; + } + + /** +* +* +* @param color +*/ + public void setColor(Color color) { + this.color = color; + } + + /** +* +* +* @param borderColor +*/ + public void setBorderColor(Color borderColor) { + this.borderColor = borderColor; + } + + /** +* +* +* @return +*/ + public int getIconWidth() { + return (size == null) ? 16 : size.width; + } + + /** +* +* +* @return +*/ + public int getIconHeight() { + return (size == null) ? 16 : size.height; + } +} diff --git a/src/com/sshtools/common/ui/ConnectionPropertiesAction.java b/src/com/sshtools/common/ui/ConnectionPropertiesAction.java new file mode 100644 index 0000000000000000000000000000000000000000..cf6099167b91371f33e6c8980719f5fe0c07e468 --- /dev/null +++ b/src/com/sshtools/common/ui/ConnectionPropertiesAction.java @@ -0,0 +1,63 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public abstract class ConnectionPropertiesAction extends StandardAction { + /** +* Creates a new ConnectionPropertiesAction object. +*/ + public ConnectionPropertiesAction() { + putValue(Action.NAME, "Connection Settings"); + putValue(Action.SMALL_ICON, + getIcon("/com/sshtools/common/ui/properties.png")); + putValue(Action.SHORT_DESCRIPTION, "Connection settings"); + putValue(Action.LONG_DESCRIPTION, + "Change the current connecting settings"); + putValue(Action.MNEMONIC_KEY, new Integer('t')); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_T, KeyEvent.ALT_MASK)); + putValue(Action.ACTION_COMMAND_KEY, "connect-properties-command"); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "Edit"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(80)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(10)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(15)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(10)); + } +} diff --git a/src/com/sshtools/common/ui/DataNotificationListener.java b/src/com/sshtools/common/ui/DataNotificationListener.java new file mode 100644 index 0000000000000000000000000000000000000000..ebd0ad7093c411302ac09850932c4fc6207062d7 --- /dev/null +++ b/src/com/sshtools/common/ui/DataNotificationListener.java @@ -0,0 +1,80 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelEventAdapter; +import com.sshtools.j2ssh.connection.ChannelEventListener; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.Timer; + + +public class DataNotificationListener extends ChannelEventAdapter { + Timer timerReceiving; + Timer timerSending; + StatusBar bar; + + public DataNotificationListener(StatusBar bar) { + this.bar = bar; + timerReceiving = new Timer(500, new ReceivingActionListener()); + timerReceiving.setRepeats(false); + timerSending = new Timer(500, new SendingActionListener()); + timerSending.setRepeats(false); + } + + public void actionPerformed(ActionEvent evt) { + bar.setReceiving(false); + } + + public void onDataReceived(Channel channel, byte[] data) { + if (!timerReceiving.isRunning()) { + bar.setReceiving(true); + timerReceiving.start(); + } + } + + public void onDataSent(Channel channel, byte[] data) { + if (!timerSending.isRunning()) { + bar.setSending(true); + timerSending.start(); + } + } + + class SendingActionListener implements ActionListener { + public void actionPerformed(ActionEvent evt) { + bar.setSending(false); + } + } + + class ReceivingActionListener implements ActionListener { + public void actionPerformed(ActionEvent evt) { + bar.setReceiving(false); + } + } +} diff --git a/src/com/sshtools/common/ui/EditAction.java b/src/com/sshtools/common/ui/EditAction.java new file mode 100644 index 0000000000000000000000000000000000000000..63686ece7e545988728b55f55f1288fe97216bf9 --- /dev/null +++ b/src/com/sshtools/common/ui/EditAction.java @@ -0,0 +1,55 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import javax.swing.Action; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class EditAction extends StandardAction { + /** +* Creates a new EditAction object. +*/ + public EditAction() { + putValue(Action.NAME, "Edit"); + putValue(Action.SMALL_ICON, + getIcon("/com/sshtools/common/ui/fileedit.png")); + putValue(Action.SHORT_DESCRIPTION, "Edit connection file"); + putValue(Action.LONG_DESCRIPTION, "Edit connection file"); + putValue(Action.MNEMONIC_KEY, new Integer('e')); + putValue(Action.ACTION_COMMAND_KEY, "edit-command"); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(6)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(false)); + } +} diff --git a/src/com/sshtools/common/ui/EmptyIcon.java b/src/com/sshtools/common/ui/EmptyIcon.java new file mode 100644 index 0000000000000000000000000000000000000000..59bbc62494c3c737241dd785bebdaa5c0d32c1c1 --- /dev/null +++ b/src/com/sshtools/common/ui/EmptyIcon.java @@ -0,0 +1,82 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class EmptyIcon implements Icon { + private int w; + private int h; + + /** +* Creates a new EmptyIcon object. +* +* @param w +* @param h +*/ + public EmptyIcon(int w, int h) { + this.w = w; + this.h = h; + } + + /** +* +* +* @param c +* @param g +* @param x +* @param y +*/ + public void paintIcon(Component c, Graphics g, int x, int y) { + } + + /** +* +* +* @return +*/ + public int getIconWidth() { + return w; + } + + /** +* +* +* @return +*/ + public int getIconHeight() { + return h; + } +} diff --git a/src/com/sshtools/common/ui/ExitAction.java b/src/com/sshtools/common/ui/ExitAction.java new file mode 100644 index 0000000000000000000000000000000000000000..846880697ebcfed99b91cc815b2a667ac909fd4b --- /dev/null +++ b/src/com/sshtools/common/ui/ExitAction.java @@ -0,0 +1,78 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ExitAction extends StandardAction { + // + private SshToolsApplication application; + private SshToolsApplicationContainer container; + + /** +* Creates a new ExitAction object. +* +* @param application +* @param container +*/ + public ExitAction(SshToolsApplication application, + SshToolsApplicationContainer container) { + this.application = application; + this.container = container; + putValue(Action.NAME, "Exit"); + putValue(Action.SMALL_ICON, getIcon("exit.png")); + putValue(Action.SHORT_DESCRIPTION, "Exit"); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.ALT_MASK)); + putValue(Action.LONG_DESCRIPTION, "Exit this window"); + putValue(Action.MNEMONIC_KEY, new Integer('x')); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(90)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(90)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(false)); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + application.closeContainer(container); + } +} diff --git a/src/com/sshtools/common/ui/FolderBar.java b/src/com/sshtools/common/ui/FolderBar.java new file mode 100644 index 0000000000000000000000000000000000000000..510b0d6c28dbc1d883c9a7dd6c1c50b17decfe49 --- /dev/null +++ b/src/com/sshtools/common/ui/FolderBar.java @@ -0,0 +1,81 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +public class FolderBar extends JPanel { + // Private instance variables + private JLabel textLabel; + + // Private instance variables + private JLabel iconLabel; + private Action action; + + public FolderBar() { + this(null, null); + } + + public FolderBar(String text) { + this(text, null); + } + + public FolderBar(String text, Icon icon) { + super(new BorderLayout()); + setOpaque(true); + setBackground(getBackground().darker()); + add(textLabel = new JLabel(), BorderLayout.CENTER); + add(iconLabel = new JLabel(), BorderLayout.WEST); + iconLabel.setFont(iconLabel.getFont().deriveFont(Font.BOLD)); + textLabel.setVerticalAlignment(JLabel.CENTER); + textLabel.setVerticalTextPosition(JLabel.BOTTOM); + textLabel.setForeground(Color.lightGray); + iconLabel.setVerticalAlignment(JLabel.CENTER); + setIcon(icon); + setText(text); + } + + public Action getAction() { + return action; + } + + public void setAction(Action action) { + this.action = action; + setIcon((Icon) action.getValue(Action.SMALL_ICON)); + setText((String) action.getValue(Action.NAME)); + } + + public void setText(String text) { + textLabel.setText(text); + } + + public void setIcon(Icon icon) { + iconLabel.setIcon(icon); + } +} diff --git a/src/com/sshtools/common/ui/GlobalOptionsTab.java b/src/com/sshtools/common/ui/GlobalOptionsTab.java new file mode 100644 index 0000000000000000000000000000000000000000..3e48d87a841b5d0eb623a5261ffb4cf76e1713ba --- /dev/null +++ b/src/com/sshtools/common/ui/GlobalOptionsTab.java @@ -0,0 +1,194 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.BorderFactory; +import javax.swing.DefaultListCellRenderer; +import javax.swing.Icon; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.UIManager; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class GlobalOptionsTab extends JPanel implements OptionsTab { + /** */ + public final static String GLOBAL_ICON = "largeglobal.png"; + + // + private JComboBox lafChooser; + + /** +* Creates a new GlobalOptionsTab object. +*/ + public GlobalOptionsTab() { + super(); + + Insets ins = new Insets(2, 2, 2, 2); + JPanel s = new JPanel(new GridBagLayout()); + GridBagConstraints gbc1 = new GridBagConstraints(); + gbc1.weighty = 1.0; + gbc1.insets = ins; + gbc1.anchor = GridBagConstraints.NORTHWEST; + gbc1.fill = GridBagConstraints.HORIZONTAL; + gbc1.weightx = 0.0; + UIUtil.jGridBagAdd(s, new JLabel("Look and feel"), gbc1, + GridBagConstraints.RELATIVE); + gbc1.weightx = 1.0; + UIUtil.jGridBagAdd(s, + lafChooser = new JComboBox( + SshToolsApplication.getAllLookAndFeelInfo()), gbc1, + GridBagConstraints.REMAINDER); + lafChooser.setRenderer(new LAFRenderer()); + + IconWrapperPanel w = new IconWrapperPanel(new ResourceIcon( + GlobalOptionsTab.class, GLOBAL_ICON), s); + + // This tab + setLayout(new BorderLayout()); + add(w, BorderLayout.CENTER); + setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + reset(); + } + + /** +* +*/ + public void reset() { + String sel = PreferencesStore.get(SshToolsApplication.PREF_LAF, + UIManager.getLookAndFeel().getClass().getName()); + + for (int i = 0; i < lafChooser.getModel().getSize(); i++) { + if (((UIManager.LookAndFeelInfo) lafChooser.getModel().getElementAt(i)).getClassName() + .equals(sel)) { + lafChooser.setSelectedIndex(i); + + break; + } + } + } + + /** +* +* +* @return +*/ + public String getTabContext() { + return "Options"; + } + + /** +* +* +* @return +*/ + public Icon getTabIcon() { + return null; + } + + /** +* +* +* @return +*/ + public String getTabTitle() { + return "Global"; + } + + /** +* +* +* @return +*/ + public String getTabToolTipText() { + return "Global options."; + } + + /** +* +* +* @return +*/ + public int getTabMnemonic() { + return 'g'; + } + + /** +* +* +* @return +*/ + public Component getTabComponent() { + return this; + } + + /** +* +* +* @return +*/ + public boolean validateTab() { + return true; + } + + /** +* +*/ + public void applyTab() { + PreferencesStore.put(SshToolsApplication.PREF_LAF, + ((UIManager.LookAndFeelInfo) lafChooser.getSelectedItem()).getClassName()); + } + + /** +* +*/ + public void tabSelected() { + } + + class LAFRenderer extends DefaultListCellRenderer { + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); + setText(((UIManager.LookAndFeelInfo) value).getName()); + + return this; + } + } +} diff --git a/src/com/sshtools/common/ui/HostsTab.java b/src/com/sshtools/common/ui/HostsTab.java new file mode 100644 index 0000000000000000000000000000000000000000..016cc613125752d7a36b2800255d9b94f69627d4 --- /dev/null +++ b/src/com/sshtools/common/ui/HostsTab.java @@ -0,0 +1,394 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.j2ssh.transport.AbstractKnownHostsKeyVerification; +import com.sshtools.j2ssh.transport.InvalidHostFileException; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.util.Iterator; +import java.util.Map; + +import javax.swing.AbstractListModel; +import javax.swing.BorderFactory; +import javax.swing.DefaultListCellRenderer; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class HostsTab extends JPanel implements OptionsTab, ActionListener { + /** */ + public final static String GLOBAL_ICON = "/com/sshtools/common/ui/largeserveridentity.png"; + + /** */ + public final static String ALLOW_ICON = "/com/sshtools/common/ui/ok.png"; + + /** */ + public final static String DENY_ICON = "/com/sshtools/common/ui/cancel.png"; + + /** */ + public final static String REMOVE_ICON = "/com/sshtools/common/ui/remove.png"; + private static Log log = LogFactory.getLog(HostsTab.class); + + // + private JList hosts; + private AbstractKnownHostsKeyVerification hostKeyVerifier; + private JButton remove; + + //private JButton deny; + private HostsListModel model; + + /** +* Creates a new HostsTab object. +* +* @param hostKeyVerifier +*/ + public HostsTab(AbstractKnownHostsKeyVerification hostKeyVerifier) { + super(); + this.hostKeyVerifier = hostKeyVerifier; + hosts = new JList(model = new HostsListModel()); + hosts.setVisibleRowCount(10); + hosts.setCellRenderer(new HostRenderer()); + hosts.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent evt) { + setAvailableActions(); + } + }); + remove = new JButton("Remove", new ResourceIcon(REMOVE_ICON)); + remove.addActionListener(this); + + //deny = new JButton("Deny", new ResourceIcon(DENY_ICON)); + //deny.addActionListener(this); + JPanel b = new JPanel(new GridBagLayout()); + b.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 0)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0, 0, 4, 0); + gbc.anchor = GridBagConstraints.NORTH; + gbc.weightx = 1.0; + UIUtil.jGridBagAdd(b, remove, gbc, GridBagConstraints.REMAINDER); + gbc.weighty = 1.0; + + //UIUtil.jGridBagAdd(b, deny, gbc, GridBagConstraints.REMAINDER); + JPanel s = new JPanel(new BorderLayout()); + s.add(new JScrollPane(hosts), BorderLayout.CENTER); + s.add(b, BorderLayout.EAST); + + IconWrapperPanel w = new IconWrapperPanel(new ResourceIcon(GLOBAL_ICON), + s); + + // This tab + setLayout(new BorderLayout()); + add(w, BorderLayout.CENTER); + setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + reset(); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == remove) { + model.removeHostAt(hosts.getSelectedIndex()); + } + + /*else if (evt.getSource() == deny) { +int i = hosts.getSelectedIndex(); +HostWrapper w = model.getHostAt(i); +w.allow = false; +model.updateHostAt(i); + }*/ + setAvailableActions(); + } + + private void setAvailableActions() { + HostWrapper w = ((model.getSize() > 0) && + (hosts.getSelectedValues().length == 1)) + ? model.getHostAt(hosts.getSelectedIndex()) : null; + remove.setEnabled(w != null); + + //deny.setEnabled( (w != null) && w.allow); + } + + /** +* +*/ + public void reset() { + ((HostsListModel) hosts.getModel()).refresh(); + setAvailableActions(); + } + + /** +* +* +* @return +*/ + public String getTabContext() { + return "Options"; + } + + /** +* +* +* @return +*/ + public Icon getTabIcon() { + return null; + } + + /** +* +* +* @return +*/ + public String getTabTitle() { + return "Hosts"; + } + + /** +* +* +* @return +*/ + public String getTabToolTipText() { + return "Allowed and denied hosts."; + } + + /** +* +* +* @return +*/ + public int getTabMnemonic() { + return 'h'; + } + + /** +* +* +* @return +*/ + public Component getTabComponent() { + return this; + } + + /** +* +* +* @return +*/ + public boolean validateTab() { + return true; + } + + /** +* +*/ + public void applyTab() { + try { + Map map = hostKeyVerifier.allowedHosts(); + String[] hosts = new String[map.keySet().size()]; + map.keySet().toArray(hosts); + log.debug("Checking if any allowed hosts need to be removed"); + + for (int i = hosts.length - 1; i >= 0; i--) { + if (log.isDebugEnabled()) { + log.debug("Looking for host " + hosts[i]); + } + + HostWrapper w = model.getHost(hosts[i]); + + if (w != null) { + if (log.isDebugEnabled()) { + log.debug("Found host " + hosts[i]); + } + + if (!w.allow) { + if (log.isDebugEnabled()) { + log.debug("Denying host " + hosts[i]); + } + + hostKeyVerifier.removeAllowedHost(hosts[i]); + + //hostKeyVerifier.denyHost(hosts[i], true); + } + } else { + if (log.isDebugEnabled()) { + log.debug("Host removed " + hosts[i]); + } + + hostKeyVerifier.removeAllowedHost(hosts[i]); + } + } + + /*java.util.List list = hostKeyVerifier.deniedHosts(); +log.debug("Checking if any denied hosts need to be removed"); + for (int i = list.size() - 1; i >= 0; i--) { +String h = (String) list.get(i); +if (log.isDebugEnabled()) { +log.debug("Looking for host " + h); +} +HostWrapper w = model.getHost(h); +if (w == null) { +if (log.isDebugEnabled()) { +log.debug("Removing host " + h); +} +hostKeyVerifier.removeDeniedHost(h); +} + }*/ + hostKeyVerifier.saveHostFile(); + } catch (InvalidHostFileException ihfe) { + log.error("Failed to store hosts file.", ihfe); + } + } + + /** +* +*/ + public void tabSelected() { + } + + class HostRenderer extends DefaultListCellRenderer { + Icon allowIcon; + Icon denyIcon; + + public HostRenderer() { + allowIcon = new ResourceIcon(ALLOW_ICON); + denyIcon = new ResourceIcon(DENY_ICON); + } + + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); + + HostWrapper w = (HostWrapper) value; + setIcon(w.allow ? allowIcon : denyIcon); + setText(w.host); + + return this; + } + } + + class HostWrapper { + boolean allow; + String host; + SshPublicKey key; + Map keys; + + HostWrapper(boolean allow, String host, Map keys) { + this.allow = allow; + this.host = host; + this.keys = keys; + } + } + + class HostsListModel extends AbstractListModel { + java.util.List hosts; + + public HostsListModel() { + hosts = new java.util.ArrayList(); + refresh(); + } + + public void refresh() { + hosts.clear(); + + Map map = hostKeyVerifier.allowedHosts(); + + for (Iterator i = map.keySet().iterator(); i.hasNext();) { + String k = (String) i.next(); + Map keys = (Map) map.get(k); + hosts.add(new HostWrapper(true, k, keys)); + } + + /* java.util.List list = hostKeyVerifier.deniedHosts(); +for (Iterator i = list.iterator(); i.hasNext(); ) { +String h = (String) i.next(); +hosts.add(new HostWrapper(false, h, null)); +}*/ + fireContentsChanged(this, 0, getSize() - 1); + } + + public HostWrapper getHost(String name) { + for (Iterator i = hosts.iterator(); i.hasNext();) { + HostWrapper w = (HostWrapper) i.next(); + + if (w.host.equals(name)) { + return w; + } + } + + return null; + } + + public int getSize() { + return hosts.size(); + } + + public Object getElementAt(int index) { + return hosts.get(index); + } + + public HostWrapper getHostAt(int index) { + return (HostWrapper) hosts.get(index); + } + + public void removeHostAt(int index) { + hosts.remove(index); + fireIntervalRemoved(this, index, index); + } + + public void updateHostAt(int index) { + fireContentsChanged(this, index, index); + } + } +} diff --git a/src/com/sshtools/common/ui/IconWrapperPanel.java b/src/com/sshtools/common/ui/IconWrapperPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..833e281f2f6f1d07e9e179572d74762c76c4c5a7 --- /dev/null +++ b/src/com/sshtools/common/ui/IconWrapperPanel.java @@ -0,0 +1,58 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class IconWrapperPanel extends JPanel { + /** +* Creates a new IconWrapperPanel object. +* +* @param icon +* @param component +*/ + public IconWrapperPanel(Icon icon, Component component) { + super(new BorderLayout()); + + // Create the west panel with the icon in it + JPanel westPanel = new JPanel(new BorderLayout()); + westPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4)); + westPanel.add(new JLabel(icon), BorderLayout.NORTH); + + // Build this panel + add(westPanel, BorderLayout.WEST); + add(component, BorderLayout.CENTER); + } +} diff --git a/src/com/sshtools/common/ui/ImagePanel.java b/src/com/sshtools/common/ui/ImagePanel.java new file mode 100644 index 0000000000000000000000000000000000000000..b1bb574106687c8f6e941c75dc6a72aa1f49a4ae --- /dev/null +++ b/src/com/sshtools/common/ui/ImagePanel.java @@ -0,0 +1,126 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.JPanel; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class ImagePanel extends JPanel { + private ResourceIcon icon; + private boolean alignBottomRight = false; + + /** +* Creates a new ImagePanel object. +* +* @param imageName +*/ + public ImagePanel(String imageName) { + icon = new ResourceIcon(imageName); + } + + /** +* Creates a new ImagePanel object. +* +* @param icon +* @param bottom +*/ + public ImagePanel(ResourceIcon icon, int bottom) { + this.icon = icon; + alignBottomRight = true; + } + + /** +* Creates a new ImagePanel object. +* +* @param imageName +* @param bottom +*/ + public ImagePanel(String imageName, int bottom) { + icon = new ResourceIcon(imageName); + alignBottomRight = true; + } + + /** +* Creates a new ImagePanel object. +*/ + public ImagePanel() { + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** +* +* +* @return +*/ + public Dimension getPreferedSize() { + Insets insets = getInsets(); + + return new Dimension(icon.getIconWidth() + insets.left + insets.right, + icon.getIconHeight() + insets.top + insets.bottom); + } + + /** +* +* +* @param g +*/ + public void paintComponent(Graphics g) { + super.paintComponent(g); + + if (icon != null) { + Insets insets = getInsets(); + + if (!alignBottomRight) { + // Paint the image at the top left hand side of the panel + icon.paintIcon(this, g, insets.left, insets.top); + } else { + // Paint the image at the bottom right hand side of the panel + icon.paintIcon(this, g, + (this.getWidth() - icon.getIconWidth()), + (this.getHeight() - icon.getIconHeight())); + } + } + } + + private void jbInit() throws Exception { + this.setBackground(Color.white); + } +} diff --git a/src/com/sshtools/common/ui/MenuAction.java b/src/com/sshtools/common/ui/MenuAction.java new file mode 100644 index 0000000000000000000000000000000000000000..47e6cf29a80828079762cd3edd62cb3281aaecf0 --- /dev/null +++ b/src/com/sshtools/common/ui/MenuAction.java @@ -0,0 +1,38 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public abstract class MenuAction extends StandardAction { + /** */ + public final static String MENU = "menu"; +} diff --git a/src/com/sshtools/common/ui/MultilineLabel.java b/src/com/sshtools/common/ui/MultilineLabel.java new file mode 100644 index 0000000000000000000000000000000000000000..44d1bca560828d84f65dfac35bf823f22c114471 --- /dev/null +++ b/src/com/sshtools/common/ui/MultilineLabel.java @@ -0,0 +1,118 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import java.util.StringTokenizer; + +import javax.swing.JLabel; +import javax.swing.JPanel; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class MultilineLabel extends JPanel { + // Private instance variables + private GridBagConstraints constraints; + private String text; + + /** +* Creates a new MultilineLabel object. +*/ + public MultilineLabel() { + this(""); + } + + /** +* Creates a new MultilineLabel object. +* +* @param text +*/ + public MultilineLabel(String text) { + super(new GridBagLayout()); + constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.NORTHWEST; + constraints.fill = GridBagConstraints.NONE; + setText(text); + } + + /** +* +* +* @param f +*/ + public void setFont(Font f) { + super.setFont(f); + + for (int i = 0; i < getComponentCount(); i++) { + getComponent(i).setFont(f); + } + } + + /** +* +* +* @param text +*/ + public void setText(String text) { + this.text = text; + removeAll(); + + StringTokenizer tok = new StringTokenizer(text, "\n"); + constraints.weighty = 0.0; + constraints.weightx = 1.0; + + while (tok.hasMoreTokens()) { + String t = tok.nextToken(); + + if (!tok.hasMoreTokens()) { + constraints.weighty = 1.0; + } + + UIUtil.jGridBagAdd(this, new JLabel(t), constraints, + GridBagConstraints.REMAINDER); + } + + revalidate(); + repaint(); + } + + /** +* +* +* @return +*/ + public String getText() { + return text; + } +} diff --git a/src/com/sshtools/common/ui/NewAction.java b/src/com/sshtools/common/ui/NewAction.java new file mode 100644 index 0000000000000000000000000000000000000000..a656afe600dd131008715fd36c4a07976e462835 --- /dev/null +++ b/src/com/sshtools/common/ui/NewAction.java @@ -0,0 +1,70 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class NewAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_NEW = "new-command"; + private final static String NAME_NEW = "New Connection"; + private final static String SMALL_ICON_NEW = "/com/sshtools/common/ui/newconnect.png"; + private final static String LARGE_ICON_NEW = ""; + private final static String SHORT_DESCRIPTION_NEW = "Create a new connection"; + private final static String LONG_DESCRIPTION_NEW = "Create a new SSH connection"; + private final static int MNEMONIC_KEY_NEW = 'C'; + + /** +* Creates a new NewAction object. +*/ + public NewAction() { + putValue(Action.NAME, NAME_NEW); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_NEW)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_NEW)); + putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION_NEW); + putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION_NEW); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.ALT_MASK)); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_NEW)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_NEW); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(1)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(0)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(0)); + } +} diff --git a/src/com/sshtools/common/ui/NewWindowAction.java b/src/com/sshtools/common/ui/NewWindowAction.java new file mode 100644 index 0000000000000000000000000000000000000000..b36d9c8c9c55e7bef3a6e63bd08f00d03b0cd87d --- /dev/null +++ b/src/com/sshtools/common/ui/NewWindowAction.java @@ -0,0 +1,90 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class NewWindowAction extends StandardAction { + /** */ + protected Log log = LogFactory.getLog(NewWindowAction.class); + + // + private SshToolsApplication application; + + /** +* Creates a new NewWindowAction object. +* +* @param application +*/ + public NewWindowAction(SshToolsApplication application) { + this.application = application; + putValue(Action.NAME, "New Window"); + putValue(Action.SMALL_ICON, + getIcon("/com/sshtools/common/ui/newwindow.png")); + putValue(LARGE_ICON, + getIcon("/com/sshtools/common/ui/largenewwindow.png")); + putValue(Action.SHORT_DESCRIPTION, "Create new window"); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.ALT_MASK)); + putValue(Action.LONG_DESCRIPTION, "Create a new SSHTerm window"); + putValue(Action.MNEMONIC_KEY, new Integer('w')); + putValue(Action.ACTION_COMMAND_KEY, "new-window"); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(90)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(0)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(90)); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + try { + application.newContainer(); + } catch (SshToolsApplicationException stae) { + log.error(stae); + } + } +} diff --git a/src/com/sshtools/common/ui/NumericTextField.java b/src/com/sshtools/common/ui/NumericTextField.java new file mode 100644 index 0000000000000000000000000000000000000000..39d403cd870f54221d97a7c37e1c46d6a8c5edb2 --- /dev/null +++ b/src/com/sshtools/common/ui/NumericTextField.java @@ -0,0 +1,501 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.Color; +import java.awt.event.FocusEvent; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.ParseException; + +import javax.swing.JTextField; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class NumericTextField extends XTextField { + private Color positiveBackground; + private DecimalFormatSymbols symbols; + + // Private instance variables + private NumberFormat numberFormat; + private boolean selectAllOnFocusGain; + private int wColumnWidth; + + /** +* Creates a new NumericTextField object. +* +* @param min +* @param max +*/ + public NumericTextField(Number min, Number max) { + this(min, max, min); + } + + /** +* Creates a new NumericTextField object. +* +* @param min +* @param max +* @param initial +* @param rightJustify +*/ + public NumericTextField(Number min, Number max, Number initial, + boolean rightJustify) { + this(min, max, initial, rightJustify, null); + } + + /** +* Creates a new NumericTextField object. +* +* @param min +* @param max +* @param initial +*/ + public NumericTextField(Number min, Number max, Number initial) { + this(min, max, initial, true); + } + + /** +* Creates a new NumericTextField object. +* +* @param min +* @param max +* @param initial +* @param rightJustify +* @param numberFormat +* +* @throws IllegalArgumentException +*/ + public NumericTextField(Number min, Number max, Number initial, + boolean rightJustify, NumberFormat numberFormat) { + super(Math.max(min.toString().length(), max.toString().length())); + setNumberFormat(numberFormat); + + if (min.getClass().equals(max.getClass()) && + max.getClass().equals(initial.getClass())) { + setDocument(new ADocument(min, max)); + setValue(initial); + } else { + throw new IllegalArgumentException( + "All arguments must be of the same class"); + } + + setRightJustify(rightJustify); + } + + /* +* Overides <code>JTextFields</codes> calculation of the width of a single +* character (M space) +* +* @return column width based on '9' +* public int getColumnWidth() { +* if (wColumnWidth==0) { +* FontMetrics metrics = getFontMetrics(getFont()); +* wColumnWidth = metrics.charWidth('W'); +* } +* return wColumnWidth; +* } +*/ + protected void processFocusEvent(FocusEvent e) { + super.processFocusEvent(e); + + if (!e.isTemporary()) { + switch (e.getID()) { + case FocusEvent.FOCUS_LOST: + + if (getNumberFormat() != null) { + String s = getNumberFormat().format(getValue()).toString(); + + if (!getText().equals(s)) { + setText(s); + } + } + + break; + + case FocusEvent.FOCUS_GAINED: + + if (isSelectAllOnFocusGain()) { + selectAll(); + } + + break; + } + } + } + + /** +* +* +* @return +*/ + public boolean isSelectAllOnFocusGain() { + return selectAllOnFocusGain; + } + + /** +* +* +* @param selectAllOnFocusGain +*/ + public void setSelectAllOnFocusGain(boolean selectAllOnFocusGain) { + this.selectAllOnFocusGain = selectAllOnFocusGain; + } + + /** +* +* +* @param max +*/ + public void setMaximum(Number max) { + ((ADocument) getDocument()).setMaximum(max); + } + + /** +* +* +* @return +*/ + public Number getMaximum() { + return ((ADocument) getDocument()).max; + } + + /** +* +* +* @param min +*/ + public void setMinimum(Number min) { + ((ADocument) getDocument()).setMinimum(min); + } + + /** +* +* +* @return +*/ + public Number getMinimum() { + return ((ADocument) getDocument()).min; + } + + /** +* +* +* @param numberFormat +*/ + public void setNumberFormat(NumberFormat numberFormat) { + this.numberFormat = numberFormat; + + if (numberFormat instanceof DecimalFormat) { + symbols = ((DecimalFormat) numberFormat).getDecimalFormatSymbols(); + } else { + symbols = new DecimalFormatSymbols(); + } + } + + /** +* +* +* @return +*/ + public NumberFormat getNumberFormat() { + return numberFormat; + } + + /** +* +* +* @param rightJustify +*/ + public void setRightJustify(boolean rightJustify) { + setHorizontalAlignment(rightJustify ? JTextField.RIGHT : JTextField.LEFT); + } + + /** +* +* +* @return +*/ + public boolean isRightJustify() { + return getHorizontalAlignment() == JTextField.RIGHT; + } + + /** +* +* +* @param s +*/ + public void setText(String s) { + ADocument doc = (ADocument) getDocument(); + Number oldValue = doc.currentVal; + + try { + doc.currentVal = doc.parse(s); + } catch (Exception e) { + e.printStackTrace(); + + return; + } + + if (oldValue != doc.currentVal) { + doc.checkingEnabled = false; + super.setText(s); + doc.checkingEnabled = true; + } + } + + /** +* +* +* @param i +*/ + public void setValue(Number i) { + setText(i.toString()); + } + + /** +* +* +* @return +*/ + public Number getValue() { + return ((ADocument) getDocument()).getValue(); + } + + // Supporting classes + class ADocument extends PlainDocument { + Number currentVal; + Number max; + Number min; + boolean checkingEnabled = true; + boolean rightJustify = true; + + public ADocument(Number min, Number max) { + this.min = min; + this.max = max; + + if (min.getClass().equals(Byte.class)) { + currentVal = new Byte((byte) 0); + } else { + if (min.getClass().equals(Short.class)) { + currentVal = new Short((short) 0); + } else { + if (min.getClass().equals(Integer.class)) { + currentVal = new Integer(0); + } else { + if (min.getClass().equals(Long.class)) { + currentVal = new Long(0L); + } else { + if (min.getClass().equals(Float.class)) { + currentVal = new Float(0f); + } else { + currentVal = new Double(0d); + } + } + } + } + } + } + + public void setMaximum(Number max) { + this.max = max; + } + + public void setMinimum(Number min) { + this.min = min; + } + + public void setRightJustify(boolean rightJustify) { + this.rightJustify = rightJustify; + } + + public boolean isRightJustify() { + return rightJustify; + } + + public Number getValue() { + return currentVal; + } + + public void insertString(int offs, String str, AttributeSet a) + throws BadLocationException { + if (str == null) { + return; + } + + if (!checkingEnabled) { + super.insertString(offs, str, a); + + return; + } + + String proposedResult = null; + + if (getLength() == 0) { + proposedResult = str; + } else { + StringBuffer currentBuffer = new StringBuffer(getText(0, + getLength())); + currentBuffer.insert(offs, str); + proposedResult = currentBuffer.toString(); + } + + try { + currentVal = parse(proposedResult); + super.insertString(offs, str, a); + } catch (Exception e) { + } + } + + public Number parse(String proposedResult) throws NumberFormatException { + Double d = new Double(0d); + + // See if the proposed result matches the number format (if any) + if (!proposedResult.equals(String.valueOf(symbols.getMinusSign())) && + (proposedResult.length() != 0)) { + if (getNumberFormat() != null) { + // Strip out everything from the proposed result other than the + // numbers and and decimal separators + StringBuffer sB = new StringBuffer(); + + for (int i = 0; i < proposedResult.length(); i++) { + char ch = proposedResult.charAt(i); + + if ((ch == symbols.getDecimalSeparator()) || + ((ch >= '0') && (ch <= '9'))) { + sB.append(ch); + } + } + + String s = sB.toString(); + + // Find out how many digits there are before the decimal place + int i = 0; + + for (; + (i < s.length()) && + (s.charAt(i) != symbols.getDecimalSeparator()); + i++) { + ; + } + + int before = i; + int after = 0; + + if (before < s.length()) { + after = s.length() - i - 1; + } + + if (before > getNumberFormat().getMaximumIntegerDigits()) { + throw new NumberFormatException( + "More digits BEFORE the decimal separator than allowed:" + + proposedResult); + } + + if (after > getNumberFormat().getMaximumFractionDigits()) { + throw new NumberFormatException( + "More digits AFTER the decimal separator than allowed:" + + proposedResult); + } + + // Now try to parse the field against the number format + try { + d = new Double(getNumberFormat().parse(proposedResult) + .doubleValue()); + } catch (ParseException pE) { + throw new NumberFormatException("Failed to parse. " + + proposedResult + pE.getMessage()); + } + } + // Just use the default parse + else { + d = new Double(proposedResult); + } + } + + // Now determine if the number if within range + if ((d.doubleValue() >= min.doubleValue()) && + (d.doubleValue() <= max.doubleValue())) { + // Now create the real type + if (min.getClass().equals(Byte.class)) { + return new Byte(d.byteValue()); + } else { + if (min.getClass().equals(Short.class)) { + return new Short(d.shortValue()); + } else { + if (min.getClass().equals(Integer.class)) { + return new Integer(d.intValue()); + } else { + if (min.getClass().equals(Long.class)) { + return new Long(d.longValue()); + } else { + if (min.getClass().equals(Float.class)) { + return new Float(d.floatValue()); + } else { + return d; + } + } + } + } + } + } else { + throw new NumberFormatException(d + + " Is out of range. Minimum is " + min.doubleValue() + + ", Maximum is " + max.doubleValue()); + } + } + + public void remove(int offs, int len) throws BadLocationException { + if (!checkingEnabled) { + super.remove(offs, len); + + return; + } + + String currentText = getText(0, getLength()); + String beforeOffset = currentText.substring(0, offs); + String afterOffset = currentText.substring(len + offs, + currentText.length()); + String proposedResult = beforeOffset + afterOffset; + + try { + currentVal = parse(proposedResult); + super.remove(offs, len); + } catch (Exception e) { + } + } + } +} diff --git a/src/com/sshtools/common/ui/OpenAction.java b/src/com/sshtools/common/ui/OpenAction.java new file mode 100644 index 0000000000000000000000000000000000000000..bec42959b92849087abb5a02dfaac146255e59a7 --- /dev/null +++ b/src/com/sshtools/common/ui/OpenAction.java @@ -0,0 +1,70 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class OpenAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_OPEN = "open-command"; + private final static String NAME_OPEN = "Open"; + private final static String SMALL_ICON_OPEN = "/com/sshtools/common/ui/fileopen.png"; + private final static String LARGE_ICON_OPEN = ""; + private final static String SHORT_DESCRIPTION_OPEN = "Open an existing connection"; + private final static String LONG_DESCRIPTION_OPEN = "Opens an existing connection from file"; + private final static int MNEMONIC_KEY_OPEN = 'O'; + + /** +* Creates a new OpenAction object. +*/ + public OpenAction() { + putValue(Action.NAME, NAME_OPEN); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_OPEN)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_OPEN)); + putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION_OPEN); + putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION_OPEN); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.ALT_MASK)); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_OPEN)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_OPEN); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(5)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(0)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(5)); + } +} diff --git a/src/com/sshtools/common/ui/Option.java b/src/com/sshtools/common/ui/Option.java new file mode 100644 index 0000000000000000000000000000000000000000..9019c88f7c86870299d7886b55be76937e1f25fc --- /dev/null +++ b/src/com/sshtools/common/ui/Option.java @@ -0,0 +1,79 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class Option { + private String text; + private String toolTipText; + private int mnemonic; + + /** +* Creates a new Option object. +* +* @param text +* @param toolTipText +* @param mnemonic +*/ + public Option(String text, String toolTipText, int mnemonic) { + this.text = text; + this.toolTipText = toolTipText; + this.mnemonic = mnemonic; + } + + /** +* +* +* @return +*/ + public String getText() { + return text; + } + + /** +* +* +* @return +*/ + public int getMnemonic() { + return mnemonic; + } + + /** +* +* +* @return +*/ + public String getToolTipText() { + return toolTipText; + } +} diff --git a/src/com/sshtools/common/ui/OptionCallback.java b/src/com/sshtools/common/ui/OptionCallback.java new file mode 100644 index 0000000000000000000000000000000000000000..af7a5271f97a6456b399511fd51229f0e2c83b11 --- /dev/null +++ b/src/com/sshtools/common/ui/OptionCallback.java @@ -0,0 +1,45 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface OptionCallback { + /** +* +* +* @param dialog +* @param option +* +* @return +*/ + public boolean canClose(OptionsDialog dialog, Option option); +} diff --git a/src/com/sshtools/common/ui/OptionsAction.java b/src/com/sshtools/common/ui/OptionsAction.java new file mode 100644 index 0000000000000000000000000000000000000000..8b6c9b16ec92e67bbb9f6617d80cd16f0681d3e1 --- /dev/null +++ b/src/com/sshtools/common/ui/OptionsAction.java @@ -0,0 +1,57 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import javax.swing.Action; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class OptionsAction extends StandardAction { + /** +* Creates a new OptionsAction object. +*/ + public OptionsAction() { + putValue(Action.NAME, "Options"); + putValue(Action.SMALL_ICON, + getIcon("/com/sshtools/common/ui/options.png")); + putValue(Action.SHORT_DESCRIPTION, "Application options"); + putValue(Action.LONG_DESCRIPTION, "Edit the application options"); + putValue(Action.MNEMONIC_KEY, new Integer('o')); + putValue(Action.ACTION_COMMAND_KEY, "options-command"); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "Tools"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(90)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(99)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(90)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(0)); + } +} diff --git a/src/com/sshtools/common/ui/OptionsDialog.java b/src/com/sshtools/common/ui/OptionsDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..b9131bc7cc794b92adf9fa5c321aa6156a2f327d --- /dev/null +++ b/src/com/sshtools/common/ui/OptionsDialog.java @@ -0,0 +1,229 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class OptionsDialog extends JDialog implements ActionListener { + private Option selectedOption; + private OptionCallback callback; + private JButton defaultButton; + + /** +* Creates a new OptionsDialog object. +* +* @param parent +* @param options +* @param message +* @param title +* @param defaultOption +* @param callback +* @param modal +* @param icon +*/ + public OptionsDialog(JDialog parent, Option[] options, Object message, + String title, Option defaultOption, OptionCallback callback, + boolean modal, Icon icon) { + super(parent, title, modal); + init(options, message, defaultOption, callback, icon); + } + + /** +* Creates a new OptionsDialog object. +* +* @param parent +* @param options +* @param message +* @param title +* @param defaultOption +* @param callback +* @param modal +* @param icon +*/ + public OptionsDialog(JFrame parent, Option[] options, Object message, + String title, Option defaultOption, OptionCallback callback, + boolean modal, Icon icon) { + super(parent, title, modal); + init(options, message, defaultOption, callback, icon); + } + + private void init(Option[] options, Object message, Option defaultOption, + OptionCallback callback, Icon icon) { + // + this.callback = callback; + + JPanel b = new JPanel(new FlowLayout(FlowLayout.RIGHT, 2, 2)); + b.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + for (int i = 0; i < options.length; i++) { + JButton button = new JButton(options[i].getText()); + + if (options[i] == defaultOption) { + button.setDefaultCapable(options[i] == defaultOption); + defaultButton = button; + } + + button.setMnemonic(options[i].getMnemonic()); + button.setToolTipText(options[i].getToolTipText()); + button.putClientProperty("option", options[i]); + button.addActionListener(this); + b.add(button); + } + + // + JPanel s = new JPanel(new BorderLayout()); + s.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + s.add(new JSeparator(JSeparator.HORIZONTAL), BorderLayout.NORTH); + s.add(b, BorderLayout.SOUTH); + + // + JPanel z = new JPanel(new BorderLayout()); + z.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + // + if (message instanceof JComponent) { + z.add((JComponent) message, BorderLayout.CENTER); + } else { + z.add(new MultilineLabel(String.valueOf(message)), + BorderLayout.CENTER); + } + + // Icon panel + JLabel i = null; + + if (icon != null) { + i = new JLabel(icon); + i.setVerticalAlignment(JLabel.NORTH); + i.setBorder(BorderFactory.createEmptyBorder(4, 4, 0, 4)); + } + + // Build this panel + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(z, BorderLayout.CENTER); + + if (i != null) { + getContentPane().add(i, BorderLayout.WEST); + } + + getContentPane().add(s, BorderLayout.SOUTH); + + // + pack(); + } + + /** +* +* +* @return +*/ + public JButton getDefaultButton() { + return defaultButton; + } + + /** +* +* +* @return +*/ + public Option getSelectedOption() { + return selectedOption; + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + selectedOption = (Option) ((JButton) evt.getSource()).getClientProperty( + "option"); + + if ((callback == null) || callback.canClose(this, selectedOption)) { + setVisible(false); + } + } + + /** +* +* +* @param parent +* @param options +* @param message +* @param title +* @param defaultOption +* @param callback +* @param icon +* +* @return +*/ + public static OptionsDialog createOptionDialog(JComponent parent, + Option[] options, Object message, String title, Option defaultOption, + OptionCallback callback, Icon icon) { + // + OptionsDialog dialog = null; + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + + if (w instanceof JFrame) { + dialog = new OptionsDialog((JFrame) w, options, message, title, + defaultOption, callback, true, icon); + } else if (w instanceof JDialog) { + dialog = new OptionsDialog((JDialog) w, options, message, title, + defaultOption, callback, true, icon); + } else { + dialog = new OptionsDialog((JFrame) null, options, message, title, + defaultOption, callback, true, icon); + } + + if (dialog.getDefaultButton() != null) { + dialog.getRootPane().setDefaultButton(dialog.getDefaultButton()); + } + + return dialog; + } +} diff --git a/src/com/sshtools/common/ui/OptionsPanel.java b/src/com/sshtools/common/ui/OptionsPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..a1959ba9a77bb465443eb747135cacd036ee3280 --- /dev/null +++ b/src/com/sshtools/common/ui/OptionsPanel.java @@ -0,0 +1,206 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class OptionsPanel extends JPanel { + // + + /** */ + protected Log log = LogFactory.getLog(OptionsPanel.class); + + // + private Tabber tabber; + private boolean cancelled; + + /** +* Creates a new OptionsPanel object. +* +* @param optionalTabs +*/ + public OptionsPanel(OptionsTab[] optionalTabs) { + super(); + tabber = new Tabber(); + + if (optionalTabs != null) { + for (int i = 0; i < optionalTabs.length; i++) { + optionalTabs[i].reset(); + addTab(optionalTabs[i]); + } + } + + // Add the common tabs + addTab(new GlobalOptionsTab()); + + // Build this panel + setLayout(new GridLayout(1, 1)); + add(tabber); + } + + /** +* +* +* @return +*/ + public boolean validateTabs() { + return tabber.validateTabs(); + } + + /** +* +*/ + public void applyTabs() { + tabber.applyTabs(); + } + + /** +* +* +* @param tab +*/ + public void addTab(OptionsTab tab) { + tabber.addTab(tab); + } + + /** +* +*/ + public void reset() { + for (int i = 0; i < tabber.getTabCount(); i++) { + ((OptionsTab) tabber.getTabAt(i)).reset(); + } + } + + /** +* +* +* @param parent +* @param optionalTabs +* +* @return +*/ + public static boolean showOptionsDialog(Component parent, + OptionsTab[] optionalTabs) { + final OptionsPanel opts = new OptionsPanel(optionalTabs); + opts.reset(); + + JDialog d = null; + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + + if (w instanceof JDialog) { + d = new JDialog((JDialog) w, "Options", true); + } else if (w instanceof JFrame) { + d = new JDialog((JFrame) w, "Options", true); + } else { + d = new JDialog((JFrame) null, "Options", true); + } + + final JDialog dialog = d; + + // Create the bottom button panel + final JButton cancel = new JButton("Cancel"); + cancel.setMnemonic('c'); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + opts.cancelled = true; + dialog.setVisible(false); + } + }); + + final JButton ok = new JButton("Ok"); + ok.setMnemonic('o'); + ok.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (opts.validateTabs()) { + dialog.setVisible(false); + } + } + }); + dialog.getRootPane().setDefaultButton(ok); + + JPanel buttonPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(6, 6, 0, 0); + gbc.weighty = 1.0; + UIUtil.jGridBagAdd(buttonPanel, ok, gbc, GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(buttonPanel, cancel, gbc, + GridBagConstraints.REMAINDER); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + southPanel.add(buttonPanel); + + // + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(opts, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + + // Show the dialog + dialog.getContentPane().setLayout(new GridLayout(1, 1)); + dialog.getContentPane().add(mainPanel); + dialog.pack(); + dialog.setResizable(true); + UIUtil.positionComponent(SwingConstants.CENTER, dialog); + dialog.setVisible(true); + + if (!opts.cancelled) { + opts.applyTabs(); + } + + return !opts.cancelled; + } +} diff --git a/src/com/sshtools/common/ui/OptionsTab.java b/src/com/sshtools/common/ui/OptionsTab.java new file mode 100644 index 0000000000000000000000000000000000000000..c25cc320394520a335be16b61b6c8a4aace5350d --- /dev/null +++ b/src/com/sshtools/common/ui/OptionsTab.java @@ -0,0 +1,40 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface OptionsTab extends Tab { + /** +* +*/ + public void reset(); +} diff --git a/src/com/sshtools/common/ui/PreferencesStore.java b/src/com/sshtools/common/ui/PreferencesStore.java new file mode 100644 index 0000000000000000000000000000000000000000..5f79163e010799c039608fc1d27b39f707161aae --- /dev/null +++ b/src/com/sshtools/common/ui/PreferencesStore.java @@ -0,0 +1,378 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.Rectangle; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.Properties; +import java.util.StringTokenizer; + +import javax.swing.JTable; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class PreferencesStore { + /** */ + protected static Log log = LogFactory.getLog(PreferencesStore.class); + + // + private static File file; + private static boolean storeAvailable; + private static Properties preferences; + + // Intialise the preferences + static { + preferences = new Properties(); + } + + /** +* +* +* @param table +* @param pref +*/ + public static void saveTableMetrics(JTable table, String pref) { + for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) { + int w = table.getColumnModel().getColumn(i).getWidth(); + put(pref + ".column." + i + ".width", String.valueOf(w)); + put(pref + ".column." + i + ".position", + String.valueOf(table.convertColumnIndexToModel(i))); + } + } + + /** +* +* +* @param table +* @param pref +* @param defaultWidths +* +* @throws IllegalArgumentException +*/ + public static void restoreTableMetrics(JTable table, String pref, + int[] defaultWidths) { + // Check the table columns may be resized correctly + if (table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF) { + throw new IllegalArgumentException( + "Table AutoResizeMode must be JTable.AUTO_RESIZE_OFF"); + } + + // Restore the table column widths and positions + for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) { + try { + table.moveColumn(table.convertColumnIndexToView(getInt(pref + + ".column." + i + ".position", i)), i); + table.getColumnModel().getColumn(i).setPreferredWidth(getInt(pref + + ".column." + i + ".width", + (defaultWidths == null) + ? table.getColumnModel().getColumn(i).getPreferredWidth() + : defaultWidths[i])); + } catch (NumberFormatException nfe) { + } + } + } + + /** +* +* +* @return +*/ + public static boolean isStoreAvailable() { + return storeAvailable; + } + + /** +* +* +* @param file +*/ + public static void init(File file) { + PreferencesStore.file = file; + + // Make sure the preferences directory exists, creating it if it doesn't + File dir = file.getParentFile(); + + if (!dir.exists()) { + log.info("Creating SSHTerm preferences directory " + + dir.getAbsolutePath()); + + if (!dir.mkdirs()) { + log.error("Preferences directory " + dir.getAbsolutePath() + + " could not be created. " + + "Preferences will not be stored"); + } + } + + storeAvailable = dir.exists(); + + // If the preferences file exists, then load it + if (storeAvailable) { + if (file.exists()) { + InputStream in = null; + + try { + in = new FileInputStream(file); + preferences.load(in); + storeAvailable = true; + } catch (IOException ioe) { + log.error(ioe); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ioe) { + } + } + } + } + // Otherwise create it + else { + savePreferences(); + } + } else { + log.warn("Preferences store not available."); + } + } + + /** +* +*/ + public static void savePreferences() { + if (file == null) { + log.error( + "Preferences not saved as PreferencesStore has not been initialise."); + } else { + OutputStream out = null; + + try { + out = new FileOutputStream(file); + preferences.store(out, "SSHTerm preferences"); + log.info("Preferences written to " + file.getAbsolutePath()); + storeAvailable = true; + } catch (IOException ioe) { + log.error(ioe); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException ioe) { + } + } + } + } + } + + /** +* +* +* @param name +* @param def +* +* @return +*/ + public static String get(String name, String def) { + return preferences.getProperty(name, def); + } + + /** +* +* +* @param name +* @param val +*/ + public static void put(String name, String val) { + preferences.put(name, val); + } + + /** +* +* +* @param name +* @param def +* +* @return +*/ + public static Rectangle getRectangle(String name, Rectangle def) { + String s = preferences.getProperty(name); + + if ((s == null) || s.equals("")) { + return def; + } else { + StringTokenizer st = new StringTokenizer(s, ","); + Rectangle r = new Rectangle(); + + try { + r.x = Integer.parseInt(st.nextToken()); + r.y = Integer.parseInt(st.nextToken()); + r.width = Integer.parseInt(st.nextToken()); + r.height = Integer.parseInt(st.nextToken()); + } catch (NumberFormatException nfe) { + log.warn("Preference is " + name + " is badly formatted", nfe); + } + + return r; + } + } + + /** +* +* +* @param name +* @param val +*/ + public static void putRectangle(String name, Rectangle val) { + preferences.put(name, + (val == null) ? "" + : (val.x + "," + val.y + "," + val.width + "," + + val.height)); + } + + /** +* +* +* @param name +* @param def +* +* @return +*/ + public static int getInt(String name, int def) { + String s = preferences.getProperty(name); + + if ((s != null) && !s.equals("")) { + try { + return Integer.parseInt(s); + } catch (NumberFormatException nfe) { + log.warn("Preference is " + name + " is badly formatted", nfe); + } + } + + return def; + } + + /** +* +* +* @param name +* @param def +* +* @return +*/ + public static double getDouble(String name, double def) { + String s = preferences.getProperty(name); + + if ((s != null) && !s.equals("")) { + try { + return Double.parseDouble(s); + } catch (NumberFormatException nfe) { + log.warn("Preference is " + name + " is badly formatted", nfe); + } + } + + return def; + } + + /** +* +* +* @param name +* @param val +*/ + public static void putInt(String name, int val) { + preferences.put(name, String.valueOf(val)); + } + + /** +* +* +* @param name +* @param val +*/ + public static void putDouble(String name, double val) { + preferences.put(name, String.valueOf(val)); + } + + /** +* +* +* @param name +* @param def +* +* @return +*/ + public static boolean getBoolean(String name, boolean def) { + return get(name, String.valueOf(def)).equals("true"); + } + + /** +* +* +* @param name +* @param val +*/ + public static void putBoolean(String name, boolean val) { + preferences.put(name, String.valueOf(val)); + } + + /** +* +* +* @param name +* +* @return +*/ + public static boolean preferenceExists(String name) { + return preferences.containsKey(name); + } + + /** +* +* +* @param name +* +* @return +*/ + public static boolean removePreference(String name) { + boolean exists = preferenceExists(name); + preferences.remove(name); + + return exists; + } +} diff --git a/src/com/sshtools/common/ui/PrintAction.java b/src/com/sshtools/common/ui/PrintAction.java new file mode 100644 index 0000000000000000000000000000000000000000..eec6bfb2722a6a37bff8b65a35686bdb56d60869 --- /dev/null +++ b/src/com/sshtools/common/ui/PrintAction.java @@ -0,0 +1,52 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +public abstract class PrintAction extends StandardAction { + public PrintAction() { + putValue(Action.NAME, "Print"); + putValue(Action.SMALL_ICON, getIcon("/com/sshtools/common/ui/print.png")); + putValue(Action.SHORT_DESCRIPTION, "Print"); + putValue(Action.LONG_DESCRIPTION, "Print the terminal screen"); + putValue(Action.MNEMONIC_KEY, new Integer('p')); + putValue(Action.ACTION_COMMAND_KEY, "print-command"); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.ALT_MASK)); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(80)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(0)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(00)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(80)); + } +} diff --git a/src/com/sshtools/common/ui/PrintPreview.java b/src/com/sshtools/common/ui/PrintPreview.java new file mode 100644 index 0000000000000000000000000000000000000000..f62c73850ed4f0359b7549fb32d73d4f16f9878f --- /dev/null +++ b/src/com/sshtools/common/ui/PrintPreview.java @@ -0,0 +1,336 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.awt.print.*; + +import javax.swing.*; +import javax.swing.border.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class PrintPreview extends JPanel implements ActionListener, Runnable { + /** */ + protected PrinterJob job; + + /** */ + protected JButton setup; + + /** */ + protected JComboBox scale; + + /** */ + protected int pageWidth; + + /** */ + protected Printable printable; + + /** */ + protected PreviewContainer previewer; + + /** */ + protected PageFormat pageFormat; + + /** */ + protected int pageHeight; + + /** */ + protected JScrollPane scrollpane; + + /** +* Creates a new PrintPreview object. +* +* @param printable +* @param pageFormat +*/ + public PrintPreview(Printable printable, PageFormat pageFormat) { + super(new BorderLayout()); + this.printable = printable; + job = PrinterJob.getPrinterJob(); + this.pageFormat = pageFormat; + + JPanel jpanel = new JPanel(new FlowLayout(0, 2, 2)); + setup = new JButton("Setup"); + setup.addActionListener(this); + setup.setMnemonic('p'); + jpanel.add(setup); + + String[] as = { "10 %", "25 %", "50 %", "75 %", "100 %", "200 %" }; + scale = new JComboBox(as); + scale.setSelectedItem(as[2]); + scale.addActionListener(this); + scale.setEditable(true); + jpanel.add(scale); + add(jpanel, BorderLayout.NORTH); + previewer = new PreviewContainer(); + + if ((pageFormat.getHeight() == 0.0D) || + (pageFormat.getWidth() == 0.0D)) { + System.err.println("Unable to determine default page size"); + + return; + } + + pageWidth = (int) pageFormat.getWidth(); + pageHeight = (int) pageFormat.getHeight(); + + byte byte0 = 50; + int i = (pageWidth * byte0) / 100; + int j = (pageHeight * byte0) / 100; + int k = 0; + + try { + do { + BufferedImage bufferedimage = new BufferedImage(pageWidth, + pageHeight, 1); + Graphics g = bufferedimage.getGraphics(); + g.setColor(Color.white); + g.fillRect(0, 0, pageWidth, pageHeight); + + if (printable.print(g, pageFormat, k) != 0) { + break; + } + + PagePreview pagepreview = new PagePreview(i, j, bufferedimage); + previewer.add(pagepreview); + k++; + } while (true); + } catch (PrinterException printerexception) { + printerexception.printStackTrace(); + System.err.println("Printing error: " + + printerexception.toString()); + } + + scrollpane = new JScrollPane(previewer); + add(scrollpane, BorderLayout.CENTER); + } + + /** +* +* +* @return +*/ + public PageFormat getPageFormat() { + return pageFormat; + } + + private void setup() { + pageFormat = job.pageDialog(pageFormat); + pageWidth = (int) pageFormat.getWidth(); + pageHeight = (int) pageFormat.getHeight(); + + Thread thread = new Thread(this); + thread.start(); + } + + /** +* +*/ + public void run() { + String s = scale.getSelectedItem().toString(); + scrollpane.setViewportView(new JLabel("Resizing")); + + if (s.endsWith("%")) { + s = s.substring(0, s.length() - 1); + } + + s = s.trim(); + + int i = 0; + + try { + i = Integer.parseInt(s); + } catch (NumberFormatException numberformatexception) { + i = 100; + } + + int j = (pageWidth * i) / 100; + int k = (pageHeight * i) / 100; + Component[] acomponent = previewer.getComponents(); + + for (int l = 0; l < acomponent.length; l++) { + if (acomponent[l] instanceof PagePreview) { + PagePreview pagepreview = (PagePreview) acomponent[l]; + pagepreview.setScaledSize(j, k); + } + } + + scrollpane.setViewportView(previewer); + } + + /** +* +* +* @param actionevent +*/ + public void actionPerformed(ActionEvent actionevent) { + if (actionevent.getSource() == setup) { + setup(); + } else if (actionevent.getSource() == scale) { + Thread thread = new Thread(this); + thread.start(); + } + } + + class PagePreview extends JPanel { + protected int m_h; + protected int m_w; + protected Image m_img; + protected Image m_source; + + public PagePreview(int i, int j, Image image) { + m_w = i; + m_h = j; + m_source = image; + m_img = m_source.getScaledInstance(m_w, m_h, 4); + m_img.flush(); + setBackground(Color.white); + setBorder(new MatteBorder(1, 1, 2, 2, Color.black)); + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + + public Dimension getPreferredSize() { + Insets insets = getInsets(); + + return new Dimension(m_w + insets.left + insets.right, + m_h + insets.top + insets.bottom); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + g.drawImage(m_img, 0, 0, this); + paintBorder(g); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public void setScaledSize(int i, int j) { + m_w = i; + m_h = j; + m_img = m_source.getScaledInstance(m_w, m_h, 4); + repaint(); + } + } + + class PreviewContainer extends JPanel { + protected int V_GAP; + protected int H_GAP; + + PreviewContainer() { + V_GAP = 10; + H_GAP = 16; + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + + public void doLayout() { + Insets insets = getInsets(); + int i = insets.left + H_GAP; + int j = insets.top + V_GAP; + int k = getComponentCount(); + + if (k == 0) { + return; + } + + Component component = getComponent(0); + Dimension dimension = component.getPreferredSize(); + int l = dimension.width; + int i1 = dimension.height; + Dimension dimension1 = getParent().getSize(); + int j1 = Math.max((dimension1.width - H_GAP) / (l + H_GAP), 1); + int k1 = k / j1; + + if ((k1 * j1) < k) { + k1++; + } + + int l1 = 0; + + for (int i2 = 0; i2 < k1; i2++) { + for (int j2 = 0; j2 < j1; j2++) { + if (l1 >= k) { + return; + } + + Component component1 = getComponent(l1++); + component1.setBounds(i, j, l, i1); + i += (l + H_GAP); + } + + j += (i1 + V_GAP); + i = insets.left + H_GAP; + } + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getPreferredSize() { + int i = getComponentCount(); + + if (i == 0) { + return new Dimension(H_GAP, V_GAP); + } + + Component component = getComponent(0); + Dimension dimension = component.getPreferredSize(); + int j = dimension.width; + int k = dimension.height; + Dimension dimension1 = getParent().getSize(); + int l = Math.max((dimension1.width - H_GAP) / (j + H_GAP), 1); + int i1 = i / l; + + if ((i1 * l) < i) { + i1++; + } + + int j1 = (l * (j + H_GAP)) + H_GAP; + int k1 = (i1 * (k + V_GAP)) + V_GAP; + Insets insets = getInsets(); + + return new Dimension(j1 + insets.left + insets.right, + k1 + insets.top + insets.bottom); + } + } +} diff --git a/src/com/sshtools/common/ui/PrintPreviewAction.java b/src/com/sshtools/common/ui/PrintPreviewAction.java new file mode 100644 index 0000000000000000000000000000000000000000..b65a44b672a064c77794b755ed411214902a4cf0 --- /dev/null +++ b/src/com/sshtools/common/ui/PrintPreviewAction.java @@ -0,0 +1,51 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +public abstract class PrintPreviewAction extends StandardAction { + public PrintPreviewAction() { + putValue(Action.NAME, "Print Preview"); + putValue(Action.SMALL_ICON, + getIcon("/com/sshtools/common/ui/printpreview.png")); + putValue(Action.SHORT_DESCRIPTION, "Print Preview"); + putValue(Action.LONG_DESCRIPTION, "Preview what would be printed"); + putValue(Action.MNEMONIC_KEY, new Integer('r')); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.ALT_MASK)); + putValue(Action.ACTION_COMMAND_KEY, "print-preview-command"); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(80)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(10)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(false)); + } +} diff --git a/src/com/sshtools/common/ui/RecordAction.java b/src/com/sshtools/common/ui/RecordAction.java new file mode 100644 index 0000000000000000000000000000000000000000..f9dbfbb3eeaa47d16c4a39b3a905a4fdaac0a415 --- /dev/null +++ b/src/com/sshtools/common/ui/RecordAction.java @@ -0,0 +1,70 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class RecordAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_RECORD = "record-command"; + private final static String NAME_RECORD = "Record"; + private final static String SMALL_ICON_RECORD = "/com/sshtools/common/ui/record.png"; + private final static String LARGE_ICON_RECORD = ""; + private final static String SHORT_DESCRIPTION_RECORD = "Record output"; + private final static String LONG_DESCRIPTION_RECORD = "Record all output to a file"; + private final static int MNEMONIC_KEY_RECORD = 'R'; + + /** +* Creates a new RecordAction object. +*/ + public RecordAction() { + putValue(Action.NAME, NAME_RECORD); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_RECORD)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_RECORD)); + putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION_RECORD); + putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION_RECORD); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.ALT_MASK)); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_RECORD)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_RECORD); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(60)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(20)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(60)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(20)); + } +} diff --git a/src/com/sshtools/common/ui/RefreshAction.java b/src/com/sshtools/common/ui/RefreshAction.java new file mode 100644 index 0000000000000000000000000000000000000000..f7567ee879d5e485ccca5452b393ac1762f0ff33 --- /dev/null +++ b/src/com/sshtools/common/ui/RefreshAction.java @@ -0,0 +1,70 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class RefreshAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_REFRESH = "refresh-command"; + private final static String NAME_REFRESH = "Refresh"; + private final static String SMALL_ICON_REFRESH = "/com/sshtools/common/ui/refresh.png"; + private final static String LARGE_ICON_REFRESH = ""; + private final static String SHORT_DESCRIPTION_REFRESH = "Refresh terminal"; + private final static String LONG_DESCRIPTION_REFRESH = "Refresh the terminal screen"; + private final static int MNEMONIC_KEY_REFRESH = 'R'; + + /** +* Creates a new RefreshAction object. +*/ + public RefreshAction() { + putValue(Action.NAME, NAME_REFRESH); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_REFRESH)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_REFRESH)); + putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION_REFRESH); + putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION_REFRESH); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_REFRESH)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_REFRESH); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_F5, KeyEvent.ALT_MASK)); + putValue(StandardAction.MENU_NAME, "View"); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(20)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(10)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(5)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(20)); + } +} diff --git a/src/com/sshtools/common/ui/ResourceIcon.java b/src/com/sshtools/common/ui/ResourceIcon.java new file mode 100644 index 0000000000000000000000000000000000000000..79fdca667a0d2482e70732c22e564326c6b4fc00 --- /dev/null +++ b/src/com/sshtools/common/ui/ResourceIcon.java @@ -0,0 +1,136 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.Image; +import java.awt.Toolkit; + +import java.io.FilePermission; + +import java.net.URL; + +import java.security.AccessControlException; +import java.security.AccessController; + +import javax.swing.ImageIcon; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class ResourceIcon extends ImageIcon { + private static Log log = LogFactory.getLog(ResourceIcon.class.getName()); + Class cls; + + /** +* Creates a new ResourceIcon object. +* +* @param cls +* @param image +*/ + public ResourceIcon(Class cls, String image) { + super(); + this.cls = cls; + + if (image.startsWith("/")) { + loadImage(image); + } else { + String path = "/" + cls.getPackage().getName(); + path = path.replace('.', '/'); + path += ("/" + image); + loadImage(path); + } + } + + /** +* Creates a new ResourceIcon object. +* +* @param url +*/ + public ResourceIcon(URL url) { + super(url); + } + + /** +* Creates a new ResourceIcon object. +* +* @param imageName +* @deprecated Having this available is now bad practice since most of our +* software is plugable; each class requesting a resource should do so from +* the class loader that loaded the class, to keep track of images a class +* should also not be requesting a resource that is outside its own package. +* +* For resources outside of a package, we should think about creating static +* helper class to store them. +* + * Use the ResourceIcon(Class cls, String image) constructor instead providing +* the class instance of the class using the image. +* +*/ + public ResourceIcon(String imageName) { + super(); + this.cls = getClass(); + loadImage(imageName); + } + + /** +* +* +* @param imageName +*/ + protected void loadImage(String imageName) { + Image image = null; + URL url = cls.getResource(imageName); + + if (url != null) { + log.debug(url.toString()); + image = Toolkit.getDefaultToolkit().getImage(url); + } else { + try { + if (System.getSecurityManager() != null) { + AccessController.checkPermission(new FilePermission( + imageName, "read")); + } + + image = Toolkit.getDefaultToolkit().getImage(imageName); + } catch (AccessControlException ace) { + log.error("Icon " + imageName + " could not be located as a " + + "resource, and the current security manager will not " + + "allow checking for a local file."); + } + } + + if (image != null) { + this.setImage(image); + } + } +} diff --git a/src/com/sshtools/common/ui/SaveAction.java b/src/com/sshtools/common/ui/SaveAction.java new file mode 100644 index 0000000000000000000000000000000000000000..bf25852affa70031cb15b3cab89e5970c2b4728b --- /dev/null +++ b/src/com/sshtools/common/ui/SaveAction.java @@ -0,0 +1,70 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.KeyEvent; + +import javax.swing.Action; +import javax.swing.KeyStroke; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class SaveAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_SAVE = "save-command"; + private final static String NAME_SAVE = "Save"; + private final static String SMALL_ICON_SAVE = "/com/sshtools/common/ui/save.png"; + private final static String LARGE_ICON_SAVE = ""; + private final static String SHORT_DESCRIPTION_SAVE = "Save the current connection"; + private final static String LONG_DESCRIPTION_SAVE = "Save the current connection properties to file"; + private final static int MNEMONIC_KEY_SAVE = 'S'; + + /** +* Creates a new SaveAction object. +*/ + public SaveAction() { + putValue(Action.NAME, NAME_SAVE); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_SAVE)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_SAVE)); + putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION_SAVE); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.ALT_MASK)); + putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION_SAVE); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_SAVE)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_SAVE); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(50)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(0)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(20)); + } +} diff --git a/src/com/sshtools/common/ui/SaveAsAction.java b/src/com/sshtools/common/ui/SaveAsAction.java new file mode 100644 index 0000000000000000000000000000000000000000..0917910d5801645a4860587f1965b098b690b7f4 --- /dev/null +++ b/src/com/sshtools/common/ui/SaveAsAction.java @@ -0,0 +1,55 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import javax.swing.Action; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class SaveAsAction extends StandardAction { + /** +* Creates a new SaveAsAction object. +*/ + public SaveAsAction() { + putValue(Action.NAME, "Save As"); + putValue(Action.SMALL_ICON, new EmptyIcon(16, 16)); + putValue(Action.SHORT_DESCRIPTION, "Save the connection"); + putValue(Action.LONG_DESCRIPTION, + "Save the connection as a different file"); + putValue(Action.MNEMONIC_KEY, new Integer('v')); + putValue(Action.ACTION_COMMAND_KEY, "saveas-command"); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(0)); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(51)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(false)); + } +} diff --git a/src/com/sshtools/common/ui/SessionManager.java b/src/com/sshtools/common/ui/SessionManager.java new file mode 100644 index 0000000000000000000000000000000000000000..c8e8dc7007f59a657fe5365e983083fceccf48b2 --- /dev/null +++ b/src/com/sshtools/common/ui/SessionManager.java @@ -0,0 +1,171 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + +import com.sshtools.j2ssh.SftpClient; +import com.sshtools.j2ssh.SshEventAdapter; +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelFactory; +import com.sshtools.j2ssh.forwarding.ForwardingClient; +import com.sshtools.j2ssh.session.SessionChannelClient; + +import java.io.IOException; + + +/** + * <p>This interface is used by the Session Provider framework to abstract + * the SshClient connection away from the session provider. This restricts + * the session to performing operations that are allowed by the controlling + * application. For instance, the provider cannot simply diconnect the + * connection, since the SshClient's disconnect method is not exposed, instead + * a <code>requestDisconnect</code> method is provided allowing the controlling + * application to simply ignore a disconnect since it may have other sessions + * open. + * </p> + * + * <p> + * Most of the methods of this interface will simply be required to call the + * identical method on SshClient. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.11 $ + */ +public interface SessionManager { + /** +* Opens a session on the managed connection. +* +* @return +* +* @throws IOException +*/ + public SessionChannelClient openSession() throws IOException; + + /** +* The session can call this method to apply any changes to the profile it +* may have made. +* +* @param profile +*/ + public void applyProfileChanges(SshToolsConnectionProfile profile); + + /** +* Opens an SftpClient on the managed connection. +* +* @return +* +* @throws IOException +*/ + public SftpClient openSftpClient() throws IOException; + + /** +* Opens a channel on the managed connection. +* +* @param channel +* +* @return +* +* @throws IOException +*/ + public boolean openChannel(Channel channel) throws IOException; + + /** +* Determine if the managed connection is still connected. +* +* @return +*/ + public boolean isConnected(); + + /** +* Called when a session wants to disconnect the connection. The manager +* implementation should ideally not diconnect unless no other sessions +* are open. +* +* @return +*/ + public boolean requestDisconnect(); + + /** +* Gets the managed connections port forwarding client. +* +* @return +*/ + public ForwardingClient getForwardingClient(); + + /** +* Send a global request +* +* @param requestname +* @param wantreply +* @param requestdata +* +* @return +* +* @throws IOException +*/ + public byte[] sendGlobalRequest(String requestname, boolean wantreply, + byte[] requestdata) throws IOException; + + /** +* Add an event handler to the managed connection +* +* @param eventHandler +*/ + public void addEventHandler(SshEventAdapter eventHandler); + + /** +* Gets the identification string supplied by the server. +* +* @return +*/ + public String getServerId(); + + /** +* Returns the guessed EOL setting of the remote computer +* @return +*/ + public int getRemoteEOL(); + + /** +* Adds a channel factory to the managed connection. +* +* @param channelType +* @param cf +* +* @throws IOException +*/ + public void allowChannelOpen(String channelType, ChannelFactory cf) + throws IOException; + + /** +* Get the current profile attached to the session. +* +* @return +*/ + public SshToolsConnectionProfile getProfile(); +} diff --git a/src/com/sshtools/common/ui/SessionProvider.java b/src/com/sshtools/common/ui/SessionProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..b37df095378e4420e911e5ab8a750cbf3c16198e --- /dev/null +++ b/src/com/sshtools/common/ui/SessionProvider.java @@ -0,0 +1,172 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + + +/** + * <p>Instances of this class are created by the <code>SessionProviderFactory</code> + * for each installed session provider. Instances of this class can be supplied + * to the <code>SessionProviderFrame</code> to create windows contains the + * session providers service</p> + * + * @author Lee David Painter + * @version $Id: SessionProvider.java,v 1.12 2003/09/22 15:57:57 martianx Exp $ + */ +public class SessionProvider { + String id; + String name; + Class cls; + String description; + char mnemonic; + ResourceIcon smallicon; + ResourceIcon largeicon; + Class[] propertypages; + int weight; + Class optionsClass; + + SessionProvider(String id, String name, Class cls, String description, + String mnemonic, String smallicon, String largeicon, + Class optionsClass, Class[] propertypages, int weight) { + this.id = id; + this.name = name; + this.cls = cls; + this.description = description; + this.mnemonic = mnemonic.charAt(0); + this.propertypages = propertypages; + this.optionsClass = optionsClass; + this.smallicon = new ResourceIcon(cls, smallicon); + this.largeicon = new ResourceIcon(cls, largeicon); + this.weight = weight; + } + + /** +* Get the name of the provider e.g. 'Terminal Session'. +* @return +*/ + public String getName() { + return name; + } + + /** +* Get the class instance for the session providers implementation. +* @return +*/ + public Class getProviderClass() { + return cls; + } + + /** +* Get an array of class instances for the providers property pages. +* @return +*/ + public Class[] getPropertyPages() { + return propertypages; + } + + /** +* Get the description of the provider e.g. 'Opens a terminal session' +* @return +*/ + public String getDescription() { + return description; + } + + /** +* Get the mnemonic character for key access +* @return +*/ + public char getMnemonic() { + return mnemonic; + } + + /** +* Get the weight of the provider. +* @return +*/ + public int getWeight() { + return weight; + } + + /** +* Get the id of the provider e.g. 'sshterm'. +* @return +*/ + public String getId() { + return id; + } + + /** +* Get the small icon of the provider. +* @return +*/ + public ResourceIcon getSmallIcon() { + return smallicon; + } + + /** +* Get the large icon of the provider. +* @return +*/ + public ResourceIcon getLargeIcon() { + return largeicon; + } + + /** +* Get the options class implementation +* @return +*/ + public Class getOptionsClass() { + return optionsClass; + } + + /** +* Compares this session provider against another object. This method +* will only return true if the object provided is an instance of +* <code>SessionProvider</code> and that the provider id and implementation +* class are equal. +* +* @param obj +* @return +*/ + public boolean equals(Object obj) { + if ((obj != null) && obj instanceof SessionProvider) { + SessionProvider provider = (SessionProvider) obj; + + return provider.id.equals(id) && + provider.getProviderClass().equals(cls); + } + + return false; + } + + /** +* Returns the name of the provider. +* @return +*/ + public String toString() { + return name; + } +} diff --git a/src/com/sshtools/common/ui/SessionProviderAction.java b/src/com/sshtools/common/ui/SessionProviderAction.java new file mode 100644 index 0000000000000000000000000000000000000000..bc9d777daddb124bc0dd077270774150071e970a --- /dev/null +++ b/src/com/sshtools/common/ui/SessionProviderAction.java @@ -0,0 +1,55 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import javax.swing.Action; + + +public class SessionProviderAction extends StandardAction { + SessionProvider provider; + + public SessionProviderAction(SessionProvider provider) { + this.provider = provider; + putValue(Action.NAME, provider.getName()); + putValue(Action.SMALL_ICON, provider.getSmallIcon()); + putValue(LARGE_ICON, provider.getLargeIcon()); + putValue(Action.SHORT_DESCRIPTION, provider.getDescription()); + putValue(Action.LONG_DESCRIPTION, provider.getDescription()); + putValue(Action.MNEMONIC_KEY, new Integer(provider.getMnemonic())); + putValue(Action.ACTION_COMMAND_KEY, provider.getName()); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "Tools"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(10)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(70)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(40)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(20)); + } + + public SessionProvider getProvider() { + return provider; + } +} diff --git a/src/com/sshtools/common/ui/SessionProviderFactory.java b/src/com/sshtools/common/ui/SessionProviderFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f21766857de4d08d4eecb48ab23f94898f321a20 --- /dev/null +++ b/src/com/sshtools/common/ui/SessionProviderFactory.java @@ -0,0 +1,193 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.IOUtil; +import com.sshtools.j2ssh.util.ExtensionClassLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.URL; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; +import java.util.Vector; + + +/** + * + * <p>This class is responsible for dynamically loading all the installed + * session providers. A session provider can be used with + * <code>SessionProviderFrame</code> to integrate an ssh service such as + * a terminal window or sftp window within another application.</p> + * + * <p>To install a session provider you should provide a special properties + * file resource at the root of your source tree called 'session.provider'.</p> + * + * <blockquote><pre> + * This properties file should have the following properties defined: + * + * provider.id= [The unique name of the provider e.g 'sshterm'] + * provider.name= [The descriptive name of the provider e.g. 'Terminal Session'] + * provider.class= [Fully qualified classname of the provider implementation] + * provider.shortdesc= [A short description of the provider] + * provider.smallicon= [The providers small icon, must be filename only and be + * placed in the same package as the provider class implementation] + * provider.largeicon= [The providers large icon, must be filename only and be + * placed in the same package as the provider class implementation] + * provider.mnemonic= [The mnemonic character] + * provider.options= [The options panel implementation, must implement + * com.sshtools.common.ui.OptionsTab] + * property.page.1= [An number of property page panels, must implement + * com.sshtools.common.ui.SshToolsConnectionTab] + * property.page.2= [More property pages added like this] + * provider.weight= [Weight setting, used to order providers] + * </pre></blockquote> + * + * </p> + * @author Lee David Painter + * @version $Id: SessionProviderFactory.java,v 1.12 2003/09/22 15:57:57 martianx Exp $ + */ +public class SessionProviderFactory { + private static Log log = LogFactory.getLog(SessionProviderFactory.class); + private static SessionProviderFactory instance; + HashMap providers = new HashMap(); + + SessionProviderFactory() { + ExtensionClassLoader classloader = ConfigurationLoader.getExtensionClassLoader(); + + try { + Enumeration en = classloader.getResources("session.provider"); + URL url = null; + Properties properties; + InputStream in; + SessionProvider provider; + String name; + String id; + + while (en.hasMoreElements()) { + try { + url = (URL) en.nextElement(); + in = url.openStream(); + properties = new Properties(); + properties.load(in); + IOUtil.closeStream(in); + + if (properties.containsKey("provider.class") && + properties.containsKey("provider.name")) { + Class cls = classloader.loadClass(properties.getProperty( + "provider.class")); + String optionsClassName = properties.getProperty( + "provider.options"); + Class optionsClass = ((optionsClassName == null) || + optionsClassName.equals("")) ? null + : classloader.loadClass(optionsClassName); + String pageclass; + int num = 1; + Vector pages = new Vector(); + + do { + pageclass = properties.getProperty("property.page." + + String.valueOf(num), null); + + if (pageclass != null) { + pages.add(classloader.loadClass(pageclass)); + num++; + } + } while (pageclass != null); + + Class[] propertypages = new Class[pages.size()]; + pages.toArray(propertypages); + name = properties.getProperty("provider.name"); + + int weight = Integer.parseInt(properties.getProperty( + "provider.weight")); + id = properties.getProperty("provider.id", name); + provider = new SessionProvider(id, name, cls, + properties.getProperty("provider.shortdesc"), + properties.getProperty("provider.mnemonic"), + properties.getProperty("provider.smallicon"), + properties.getProperty("provider.largeicon"), + optionsClass, propertypages, weight); + providers.put(id, provider); + log.info("Installed " + provider.getName() + + " session provider"); + } + } catch (ClassNotFoundException ex) { + log.warn("Session provider class not found", ex); + } catch (IOException ex) { + log.warn("Failed to read " + url.toExternalForm(), ex); + } + } + } catch (IOException ex) { + } + } + + /** +* Get all the installed SessionProvider's. +* +* @return A list containing instances of <code>SessionProvider</code> +*/ + public List getSessionProviders() { + return new Vector(providers.values()); + } + + /** +* <p>Get a <code>SessionProvider</code> by its id. The id is defined by the + * provider.id property in the providers 'session.provider' resource file.</p> +* +* <p>Session providers that are currently defined within the SSHTools source +* tree are:<br> +* <blockquote><pre> +* sshterm - Terminal session provider +* shift - SFTP session provider +* tunneling - Secure port forwarding provider +* sshvnc - VNC session provider +* </pre></blockquote> +* +* @param id the id of the SessionProvider. +* @return +*/ + public SessionProvider getProvider(String id) { + return (SessionProvider) providers.get(id); + } + + /** +* Get the one time instance of the factory. +* @return +*/ + public static SessionProviderFactory getInstance() { + return (instance == null) ? (instance = new SessionProviderFactory()) + : instance; + } +} diff --git a/src/com/sshtools/common/ui/SessionProviderFrame.java b/src/com/sshtools/common/ui/SessionProviderFrame.java new file mode 100644 index 0000000000000000000000000000000000000000..228d361a4479ae57fcdf85972442d82acff51ad5 --- /dev/null +++ b/src/com/sshtools/common/ui/SessionProviderFrame.java @@ -0,0 +1,287 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + +import com.sshtools.j2ssh.SftpClient; +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.SshEventAdapter; +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelFactory; +import com.sshtools.j2ssh.forwarding.ForwardingClient; +import com.sshtools.j2ssh.session.SessionChannelClient; + +import java.awt.BorderLayout; + +import java.io.IOException; + + +/** + * <p>This frame class embeds a SessionProvider and manages the connection + * on behalf of the caller. To invoke a session provider from an external + * application is a straight forward procedure. Assuming that the connection + * has already been established [see SshClient] you can invoke a frame using + * the following code:</p> + * + * <blockquote><pre> + * // Create an SshClient connection + * SshClient ssh = new SshClient(); + * + * // Connection code goes here - see SshClient for more details + * + * SessionProviderFrame frame = new SessionProviderFrame(null, + * new SshToolsConnectionProfile(), + * ssh, + * SessionProviderFactory.getInstance().getProvider("sshterm")); + * frame.pack(); + * frame.show(); + * </pre></blockquote> + * + * @author Lee David Painter + * @version $Id: SessionProviderFrame.java,v 1.9 2003/11/16 19:30:08 rpernavas Exp $ + */ +public class SessionProviderFrame extends SshToolsApplicationFrame + implements SessionManager { + // Private instance variables + private SshToolsApplicationSessionPanel panel; + private SessionProvider provider; + private SshToolsConnectionProfile profile; + private SshClient ssh; + private boolean disconnectOnClose = false; + + /** +* Construct a new Session Provider frame. +* +* @param app The SshToolsApplication instance, can be null +* @param profile The profile of the connection +* @param ssh the client connection +* @param provider the provider instance +* @throws IOException +* @throws SshToolsApplicationException +*/ + public SessionProviderFrame(SshToolsConnectionProfile profile, + SshClient ssh, SessionProvider provider) + throws IOException, SshToolsApplicationException { + try { + this.provider = provider; + this.ssh = ssh; + this.profile = profile; + setIconImage(provider.getSmallIcon().getImage()); + setTitle(provider.getName() + " - " + + ssh.getConnectionProperties().getHost()); + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(panel = (SshToolsApplicationSessionPanel) provider.getProviderClass() + .newInstance(), + BorderLayout.CENTER); + + return; + } catch (IllegalAccessException ex) { + } catch (InstantiationException ex) { + } + + throw new SshToolsApplicationException("Failed to create instance of " + + provider.getProviderClass().getName()); + } + + /** +* Initialize the frame and open the remote session +* @param app the application object, can be null +* @return +* @throws IOException +* @throws SshToolsApplicationException +*/ + public boolean initFrame(SshToolsApplication app) + throws IOException, SshToolsApplicationException { + panel.setCurrentConnectionProfile(profile); + panel.init(app); + init(app, panel); + pack(); + + return panel.openSession(this, profile); + } + + /** +* Get the attached session provider panel. +* @return +*/ + public SshToolsApplicationSessionPanel getSessionPanel() { + return panel; + } + + /** +* Returns the guessed EOL setting of the remote computer +* @return +*/ + public int getRemoteEOL() { + return ssh.getRemoteEOL(); + } + + /** +* Called by the application framework when testing exit state +* @return +*/ + public boolean canExit() { + return panel.canClose(); + } + + /** + * Called by the framework when exiting. Can also be called to close the session. +*/ + public void exit() { + panel.close(); + dispose(); + } + + /** +* Implementation of the SessionManager method, simply calls the SshClient +* openSession method. +* @return +* @throws IOException +*/ + public SessionChannelClient openSession() throws IOException { + return ssh.openSessionChannel(); + } + + /** + * Implementation of the SessionManager method, this does nothing. Overide this + * method to provide additional functionality to save changes made by the session +* to the profile. +* +* @param profile +*/ + public void applyProfileChanges(SshToolsConnectionProfile profile) { + } + + /** +* When the session closes, should the connection be disconnected? +* @param disconnectOnClose +*/ + public void setDisconnectOnClose(boolean disconnectOnClose) { + this.disconnectOnClose = disconnectOnClose; + } + + /** + * Implementation of the SessionManager method, this simply calls the SshClient +* method openSftpClient. +* @return +* @throws IOException +*/ + public SftpClient openSftpClient() throws IOException { + return ssh.openSftpClient(); + } + + /** + * Implementation of the SessionManager method, this simply calls the SshClient +* method openChannel. +* @param channel +* @return +* @throws IOException +*/ + public boolean openChannel(Channel channel) throws IOException { + return ssh.openChannel(channel); + } + + /** + * Implementation of the SessionManager method, this simply calls the SshClient +* method isConnected. +* @return +*/ + public boolean isConnected() { + return ssh.isConnected(); + } + + /** +* Implementation of the SessionManager method, this simply returns false. +* Overide to change this behaviour +* +* @return +*/ + public boolean requestDisconnect() { + return disconnectOnClose; + } + + /** +* Implementation of the SessionManager method, simply calls the SshClient +* method getForwardingClient. +* @return +*/ + public ForwardingClient getForwardingClient() { + return ssh.getForwardingClient(); + } + + /** +* Implementation of the SessionManager method, simply calls the SshClient +* method sendGlobalRequest. +* @param requestname +* @param wantreply +* @param requestdata +* @return +* @throws IOException +*/ + public byte[] sendGlobalRequest(String requestname, boolean wantreply, + byte[] requestdata) throws IOException { + return ssh.sendGlobalRequest(requestname, wantreply, requestdata); + } + + /** +* Implementation of the SessionManager method, simply calls the SshClient +* method addEventHandler. +* @param eventHandler +*/ + public void addEventHandler(SshEventAdapter eventHandler) { + ssh.addEventHandler(eventHandler); + } + + /** +* Implemenation of the SessionManager method, simply calls the SshClient +* method getServerId. +* @return +*/ + public String getServerId() { + return ssh.getServerId(); + } + + /** +* Implemenation of the SessionManager method, simply calls the SshClient +* method allowChannelOpen. +* @param channelType +* @param cf +* @throws IOException +*/ + public void allowChannelOpen(String channelType, ChannelFactory cf) + throws IOException { + ssh.allowChannelOpen(channelType, cf); + } + + /** +* Gets the profile currently attached to the frame. +* @return +*/ + public SshToolsConnectionProfile getProfile() { + return profile; + } +} diff --git a/src/com/sshtools/common/ui/SessionProviderInternalFrame.java b/src/com/sshtools/common/ui/SessionProviderInternalFrame.java new file mode 100644 index 0000000000000000000000000000000000000000..6913d757701edfb0c5b17405858e45a04d30aece --- /dev/null +++ b/src/com/sshtools/common/ui/SessionProviderInternalFrame.java @@ -0,0 +1,283 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.io.IOException; +import java.awt.BorderLayout; +import com.sshtools.common.configuration.SshToolsConnectionProfile; +import com.sshtools.j2ssh.SftpClient; +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.SshEventAdapter; +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelFactory; +import com.sshtools.j2ssh.forwarding.ForwardingClient; +import com.sshtools.j2ssh.session.SessionChannelClient; + +/** + * <p>This frame class embeds a SessionProvider and manages the connection + * on behalf of the caller. To invoke a session provider from an external + * application is a straight forward procedure. Assuming that the connection + * has already been established [see SshClient] you can invoke a frame using + * the following code:</p> + * + * <blockquote><pre> + * // Create an SshClient connection + * SshClient ssh = new SshClient(); + * + * // Connection code goes here - see SshClient for more details + * + * SessionProviderFrame frame = new SessionProviderFrame(null, + * new SshToolsConnectionProfile(), + * ssh, + * SessionProviderFactory.getInstance().getProvider("sshterm")); + * frame.pack(); + * frame.show(); + * </pre></blockquote> + * + * @author Lee David Painter + * @version $Id: SessionProviderInternalFrame.java,v 1.3 2003/09/24 11:26:32 martianx Exp $ + */ +public class SessionProviderInternalFrame + extends SshToolsApplicationInternalFrame + implements SessionManager { + // Private instance variables + private SshToolsApplicationSessionPanel panel; + private SessionProvider provider; + private SshToolsConnectionProfile profile; + private SshClient ssh; + private boolean disconnectOnClose = false; + /** + * Construct a new Session Provider frame. + * + * @param app The SshToolsApplication instance, can be null + * @param profile The profile of the connection + * @param ssh the client connection + * @param provider the provider instance + * @throws IOException + * @throws SshToolsApplicationException + */ + public SessionProviderInternalFrame(SshToolsConnectionProfile profile, + SshClient ssh, + SessionProvider provider) throws + IOException, SshToolsApplicationException { + try { + this.provider = provider; + this.ssh = ssh; + this.profile = profile; + //setIconImage(provider.getSmallIcon().getImage()); + setTitle(provider.getName() + " - " + + ssh.getConnectionProperties().getHost()); + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(panel = (SshToolsApplicationSessionPanel) + provider.getProviderClass(). + newInstance(), BorderLayout.CENTER); + return; + } + catch (IllegalAccessException ex) { + } + catch (InstantiationException ex) { + } + throw new SshToolsApplicationException("Failed to create instance of " + + + provider.getProviderClass().getName()); + } + + /** + * Initialize the frame and open the remote session + * @param app the application object, can be null + * @return + * @throws IOException + * @throws SshToolsApplicationException + */ + public boolean initFrame(SshToolsApplication app) throws IOException, + SshToolsApplicationException { + panel.init(app); + init(app, panel); + pack(); + return panel.openSession(this, profile); + } + + /** + * Get the attached session provider panel. + * @return + */ + public SshToolsApplicationSessionPanel getSessionPanel() { + return panel; + } + + /** + * Called by the application framework when testing exit state + * @return + */ + public boolean canExit() { + return panel.canClose(); + } + + /** + * Called by the framework when exiting. Can also be called to close the session. + */ + public void exit() { + panel.close(); + dispose(); + } + + /** + * Implementation of the SessionManager method, simply calls the SshClient + * openSession method. + * @return + * @throws IOException + */ + public SessionChannelClient openSession() throws IOException { + return ssh.openSessionChannel(); + } + + /** + * Returns the guessed EOL setting of the remote computer + * @return + */ + public int getRemoteEOL() { + return ssh.getRemoteEOL(); + } + + /** + * Implementation of the SessionManager method, this does nothing. Overide this + * method to provide additional functionality to save changes made by the session + * to the profile. + * + * @param profile + */ + public void applyProfileChanges(SshToolsConnectionProfile profile) { + } + + /** + * Implementation of the SessionManager method, this simply calls the SshClient + * method openSftpClient. + * @return + * @throws IOException + */ + public SftpClient openSftpClient() throws IOException { + return ssh.openSftpClient(); + } + + /** + * Implementation of the SessionManager method, this simply calls the SshClient + * method openChannel. + * @param channel + * @return + * @throws IOException + */ + public boolean openChannel(Channel channel) throws IOException { + return ssh.openChannel(channel); + } + + /** + * Implementation of the SessionManager method, this simply calls the SshClient + * method isConnected. + * @return + */ + public boolean isConnected() { + return ssh.isConnected(); + } + + /** + * When the session closes, should the connection be disconnected? + * @param disconnectOnClose + */ + public void setDisconnectOnClose(boolean disconnectOnClose) { + this.disconnectOnClose = disconnectOnClose; + } + + /** + * Implementation of the SessionManager method, this simply returns false. + * Overide to change this behaviour + * + * @return + */ + public boolean requestDisconnect() { + return disconnectOnClose; + } + + /** + * Implementation of the SessionManager method, simply calls the SshClient + * method getForwardingClient. + * @return + */ + public ForwardingClient getForwardingClient() { + return ssh.getForwardingClient(); + } + + /** + * Implementation of the SessionManager method, simply calls the SshClient + * method sendGlobalRequest. + * @param requestname + * @param wantreply + * @param requestdata + * @return + * @throws IOException + */ + public byte[] sendGlobalRequest(String requestname, boolean wantreply, + byte[] requestdata) throws IOException { + return ssh.sendGlobalRequest(requestname, wantreply, requestdata); + } + + /** + * Implementation of the SessionManager method, simply calls the SshClient + * method addEventHandler. + * @param eventHandler + */ + public void addEventHandler(SshEventAdapter eventHandler) { + ssh.addEventHandler(eventHandler); + } + + /** + * Implemenation of the SessionManager method, simply calls the SshClient + * method getServerId. + * @return + */ + public String getServerId() { + return ssh.getServerId(); + } + + /** + * Implemenation of the SessionManager method, simply calls the SshClient + * method allowChannelOpen. + * @param channelType + * @param cf + * @throws IOException + */ + public void allowChannelOpen(String channelType, ChannelFactory cf) throws + IOException { + ssh.allowChannelOpen(channelType, cf); + } + + /** + * Gets the profile currently attached to the frame. + * @return + */ + public SshToolsConnectionProfile getProfile() { + return profile; + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplication.java b/src/com/sshtools/common/ui/SshToolsApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..61fd4e3922f7f7dd0c5d8f82aca39eaac310a2e6 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplication.java @@ -0,0 +1,657 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.mru.MRUList; +import com.sshtools.common.mru.MRUListModel; +import com.sshtools.common.util.BrowserLauncher; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.IOUtil; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +import java.security.AccessControlException; +import java.security.AccessController; + +import java.util.Iterator; +import java.util.Vector; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; + + +/** + * An abstract application class that provides container management, look + * and feel configuration and most recently used menus. + * + * @author Brett Smith + * @version $Revision: 1.19 $ + */ +public abstract class SshToolsApplication { + /** */ + public final static String PREF_CONNECTION_LAST_HOST = "apps.connection.lastHost"; + + /** */ + public final static String PREF_CONNECTION_LAST_USER = "apps.connection.lastUser"; + + /** */ + public final static String PREF_CONNECTION_LAST_PORT = "apps.connection.lastPort"; + + /** */ + public final static String PREF_CONNECTION_LAST_KEY = "apps.connection.lastKey"; + + /** */ + public final static String PREF_LAF = "apps.laf"; + + /** */ + public final static String CROSS_PLATFORM_LAF = "CROSS_PLATFORM"; + + /** */ + public final static String DEFAULT_LAF = "DEFAULT"; + + /** */ + public final static String SYSTEM_LAF = "SYSTEM"; + + /** */ + protected static Vector containers = new Vector(); + + /** */ + protected static Log log = LogFactory.getLog(SshToolsApplication.class); + + /** */ + protected static MRUListModel mruModel; + private static UIManager.LookAndFeelInfo[] allLookAndFeelInfo; + + static { + UIManager.LookAndFeelInfo[] i; + + try { + i = UIManager.getInstalledLookAndFeels(); + } catch (Throwable t) { + i = new UIManager.LookAndFeelInfo[0]; + } + + allLookAndFeelInfo = new UIManager.LookAndFeelInfo[i.length + 3]; + System.arraycopy(i, 0, allLookAndFeelInfo, 0, i.length); + allLookAndFeelInfo[i.length] = new UIManager.LookAndFeelInfo("Default", + DEFAULT_LAF); + + allLookAndFeelInfo[i.length + 1] = new UIManager.LookAndFeelInfo("Cross Platform", + CROSS_PLATFORM_LAF); + allLookAndFeelInfo[i.length + 2] = new UIManager.LookAndFeelInfo("System", + SYSTEM_LAF); + } + + /** */ + protected Class panelClass; + + /** */ + protected Class defaultContainerClass; + + /** */ + protected java.util.List additionalOptionsTabs; + + /** +* Creates a new SshToolsApplication object. +* +* @param panelClass +* @param defaultContainerClass +*/ + public SshToolsApplication(Class panelClass, Class defaultContainerClass) { + this.panelClass = panelClass; + this.defaultContainerClass = defaultContainerClass; + additionalOptionsTabs = new java.util.ArrayList(); + + try { + if (System.getSecurityManager() != null) { + AccessController.checkPermission(new FilePermission( + "<<ALL FILES>>", "write")); + } + + File a = getApplicationPreferencesDirectory(); + + if (a == null) { + throw new AccessControlException( + "Application preferences directory not specified."); + } + + InputStream in = null; + MRUList mru = new MRUList(); + + try { + File f = new File(a, getApplicationName() + ".mru"); + + if (f.exists()) { + if (log.isDebugEnabled()) { + log.debug("Loading MRU from " + f.getAbsolutePath()); + } + + in = new FileInputStream(f); + mru.reload(in); + } else { + if (log.isDebugEnabled()) { + log.debug("MRU file " + f.getAbsolutePath() + + " doesn't exist, creating empty list"); + } + } + } catch (Exception e) { + log.error("Could not load MRU list.", e); + } finally { + IOUtil.closeStream(in); + } + + mruModel = new MRUListModel(); + mruModel.setMRUList(mru); + } catch (AccessControlException ace) { + log.error("Could not load MRU.", ace); + } + } + + /** +* +* +* @return +*/ + public static UIManager.LookAndFeelInfo[] getAllLookAndFeelInfo() { + return allLookAndFeelInfo; + } + + /** +* +* +* @return +*/ + public MRUListModel getMRUModel() { + return mruModel; + } + + /** +* +* +* @return +*/ + public abstract String getApplicationName(); + + /** +* +* +* @return +*/ + public abstract String getApplicationVersion(); + + /** +* +* +* @return +*/ + public abstract Icon getApplicationLargeIcon(); + + /** +* +* +* @return +*/ + public abstract String getAboutLicenseDetails(); + + /** +* +* +* @return +*/ + public abstract String getAboutURL(); + + /** +* +* +* @return +*/ + public abstract String getAboutAuthors(); + + /** +* +* +* @return +*/ + public abstract File getApplicationPreferencesDirectory(); + + /** +* +* +* @return +*/ + public OptionsTab[] getAdditionalOptionsTabs() { + OptionsTab[] t = new OptionsTab[additionalOptionsTabs.size()]; + additionalOptionsTabs.toArray(t); + + return t; + } + + /** +* +* +* @param tab +*/ + public void addAdditionalOptionsTab(OptionsTab tab) { + if (!additionalOptionsTabs.contains(tab)) { + additionalOptionsTabs.add(tab); + } + } + + /** +* +* +* @param tab +*/ + public void removeAdditionalOptionsTab(OptionsTab tab) { + additionalOptionsTabs.remove(tab); + } + + /** +* +* +* @param title +*/ + public void removeAdditionalOptionsTab(String title) { + OptionsTab t = getOptionsTab(title); + + if (t != null) { + removeAdditionalOptionsTab(t); + } + } + + /** +* +* +* @param title +* +* @return +*/ + public OptionsTab getOptionsTab(String title) { + for (Iterator i = additionalOptionsTabs.iterator(); i.hasNext();) { + OptionsTab t = (OptionsTab) i.next(); + + if (t.getTabTitle().equals(title)) { + return t; + } + } + + return null; + } + + /** +* +*/ + public void exit() { + log.debug("Exiting application"); + PreferencesStore.savePreferences(); + + FileOutputStream out = null; + File a = getApplicationPreferencesDirectory(); + + if (a != null) { + try { + File f = new File(getApplicationPreferencesDirectory(), + getApplicationName() + ".mru"); + ; + + if (log.isDebugEnabled()) { + log.debug("Saving MRU to " + f.getAbsolutePath()); + } + + out = new FileOutputStream(f); + + PrintWriter w = new PrintWriter(out, true); + w.println(mruModel.getMRUList().toString()); + } catch (IOException ioe) { + log.error("Could not save MRU. ", ioe); + } finally { + IOUtil.closeStream(out); + } + } else { + log.debug( + "Not saving preferences because no preferences directory is available."); + } + + System.exit(0); + } + + /** +* +* +* @return +*/ + public int getContainerCount() { + return containers.size(); + } + + /** +* +* +* @param idx +* +* @return +*/ + public SshToolsApplicationContainer getContainerAt(int idx) { + return (SshToolsApplicationContainer) containers.elementAt(idx); + } + + /** +* +* +* @param panel +* +* @return +*/ + public SshToolsApplicationContainer getContainerForPanel( + SshToolsApplicationPanel panel) { + for (Iterator i = containers.iterator(); i.hasNext();) { + SshToolsApplicationContainer c = (SshToolsApplicationContainer) i.next(); + + if (c.getApplicationPanel() == panel) { + return c; + } + } + + return null; + } + + /** +* +* +* @param container +*/ + public void closeContainer(SshToolsApplicationContainer container) { + if (log.isDebugEnabled()) { + log.debug("Asking " + container + " if it can close"); + } + + if (container.getApplicationPanel().canClose()) { + if (log.isDebugEnabled()) { + log.debug("Closing"); + + for (Iterator i = containers.iterator(); i.hasNext();) { + log.debug(i.next() + " is currently open"); + } + } + + container.getApplicationPanel().close(); + container.closeContainer(); + containers.removeElement(container); + + if (containers.size() == 0) { + exit(); + } else { + log.debug( + "Not closing completely because there are containers still open"); + + for (Iterator i = containers.iterator(); i.hasNext();) { + log.debug(i.next() + " is still open"); + } + } + } + } + + /** +* Show an 'About' dialog +* +* +*/ + public void showAbout(Component parent) { + JPanel p = new JPanel(new GridBagLayout()); + p.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + GridBagConstraints gBC = new GridBagConstraints(); + gBC.anchor = GridBagConstraints.CENTER; + gBC.fill = GridBagConstraints.HORIZONTAL; + gBC.insets = new Insets(1, 1, 1, 1); + + JLabel a = new JLabel(getApplicationName()); + a.setFont(a.getFont().deriveFont(24f)); + UIUtil.jGridBagAdd(p, a, gBC, GridBagConstraints.REMAINDER); + + JLabel v = new JLabel(ConfigurationLoader.getVersionString( + getApplicationName(), getApplicationVersion())); + v.setFont(v.getFont().deriveFont(10f)); + UIUtil.jGridBagAdd(p, v, gBC, GridBagConstraints.REMAINDER); + + MultilineLabel x = new MultilineLabel(getAboutAuthors()); + x.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 0)); + x.setFont(x.getFont().deriveFont(12f)); + UIUtil.jGridBagAdd(p, x, gBC, GridBagConstraints.REMAINDER); + + MultilineLabel c = new MultilineLabel(getAboutLicenseDetails()); + c.setFont(c.getFont().deriveFont(10f)); + UIUtil.jGridBagAdd(p, c, gBC, GridBagConstraints.REMAINDER); + + final JLabel h = new JLabel(getAboutURL()); + h.setForeground(Color.blue); + h.setFont(new Font(h.getFont().getName(), Font.BOLD, 10)); + h.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + h.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + try { + BrowserLauncher.openURL(getAboutURL()); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + }); + UIUtil.jGridBagAdd(p, h, gBC, GridBagConstraints.REMAINDER); + JOptionPane.showMessageDialog(parent, p, "About", + JOptionPane.PLAIN_MESSAGE, getApplicationLargeIcon()); + } + + /** +* +* +* @return +* +* @throws SshToolsApplicationException +*/ + public SshToolsApplicationContainer newContainer() + throws SshToolsApplicationException { + SshToolsApplicationContainer container = null; + + try { + container = (SshToolsApplicationContainer) defaultContainerClass.newInstance(); + newContainer(container); + + return container; + } catch (Throwable t) { + throw new SshToolsApplicationException(t); + } + } + + /** +* +* +* @param container +* +* @throws SshToolsApplicationException +*/ + public void newContainer(SshToolsApplicationContainer container) + throws SshToolsApplicationException { + try { + SshToolsApplicationPanel panel = (SshToolsApplicationPanel) panelClass.newInstance(); + panel.init(this); + panel.rebuildActionComponents(); + panel.setAvailableActions(); + container.init(this, panel); + panel.setContainer(container); + + if (!container.isContainerVisible()) { + container.setContainerVisible(true); + } + + containers.addElement(container); + } catch (Throwable t) { + throw new SshToolsApplicationException(t); + } + } + + /** +* +* +* @param container +* @param newContainerClass +* +* @return +* +* @throws SshToolsApplicationException +*/ + public SshToolsApplicationContainer convertContainer( + SshToolsApplicationContainer container, Class newContainerClass) + throws SshToolsApplicationException { + log.info("Converting container of class " + + container.getClass().getName() + " to " + + newContainerClass.getName()); + + int idx = containers.indexOf(container); + + if (idx == -1) { + throw new SshToolsApplicationException( + "Container is not being manager by the application."); + } + + SshToolsApplicationContainer newContainer = null; + + try { + container.closeContainer(); + + SshToolsApplicationPanel panel = container.getApplicationPanel(); + newContainer = (SshToolsApplicationContainer) newContainerClass.newInstance(); + newContainer.init(this, panel); + panel.setContainer(newContainer); + + if (!newContainer.isContainerVisible()) { + newContainer.setContainerVisible(true); + } + + containers.setElementAt(newContainer, idx); + + return newContainer; + } catch (Throwable t) { + throw new SshToolsApplicationException(t); + } + } + + /** +* +* +* @param args +* +* @throws SshToolsApplicationException +*/ + public void init(String[] args) throws SshToolsApplicationException { + File f = getApplicationPreferencesDirectory(); + + if (f != null) { + // + PreferencesStore.init(new File(f, + getApplicationName() + ".properties")); + log.info("Preferences will be saved to " + f.getAbsolutePath()); + } else { + log.warn("No preferences can be saved."); + } + + try { + setLookAndFeel(PreferencesStore.get(PREF_LAF, SYSTEM_LAF)); + UIManager.put("OptionPane.errorIcon", + new ResourceIcon(SshToolsApplication.class, "dialog-error4.png")); + UIManager.put("OptionPane.informationIcon", + new ResourceIcon(SshToolsApplication.class, + "dialog-information.png")); + UIManager.put("OptionPane.warningIcon", + new ResourceIcon(SshToolsApplication.class, + "dialog-warning2.png")); + UIManager.put("OptionPane.questionIcon", + new ResourceIcon(SshToolsApplication.class, + "dialog-question3.png")); + } catch (Throwable t) { + log.error(t); + } + } + + /** +* +* +* @param className +* +* @throws Exception +*/ + public static void setLookAndFeel(String className) + throws Exception { + LookAndFeel laf = null; + + if (!className.equals(DEFAULT_LAF)) { + if (className.equals(SYSTEM_LAF)) { + String systemLaf = UIManager.getSystemLookAndFeelClassName(); + log.debug("System Look And Feel is " + systemLaf); + laf = (LookAndFeel) Class.forName(systemLaf).newInstance(); + } else if (className.equals(CROSS_PLATFORM_LAF)) { + String crossPlatformLaf = UIManager.getCrossPlatformLookAndFeelClassName(); + log.debug("Cross Platform Look And Feel is " + + crossPlatformLaf); + laf = (LookAndFeel) Class.forName(crossPlatformLaf).newInstance(); + } else { + laf = (LookAndFeel) Class.forName(className).newInstance(); + } + } + + // Now actually set the look and feel + if (laf != null) { + log.info("Setting look and feel " + laf.getName() + " (" + + laf.getClass().getName() + ")"); + UIManager.setLookAndFeel(laf); + UIManager.put("EditorPane.font", UIManager.getFont("TextArea.font")); + } + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationApplet.java b/src/com/sshtools/common/ui/SshToolsApplicationApplet.java new file mode 100644 index 0000000000000000000000000000000000000000..4e9416e6d79a900828798c58aad358963c8663a3 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationApplet.java @@ -0,0 +1,470 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.util.PropertyUtil; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.util.StringTokenizer; + +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JSeparator; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public abstract class SshToolsApplicationApplet extends JApplet { + // eurgghh! + + /** */ + public final static String[][] PARAMETER_INFO = { + { + "sshapps.log.file", "string", + "Logging output destination. Defaults to @console@" + }, + { + "sshapps.log.level", "string", + "Logging level. DEBUG,FATAL,ERROR,WARN,INFO,DEBUG or OFF. Defaults to OFF" + }, + { + "sshapps.ui.informationPanel.background", "hex color", + "Set the background color of the 'information panel'" + }, + { + "sshapps.ui.informationPanel.foreground", "boolean", + "Set the foreground color of the 'information panel'" + }, + { + "sshapps.ui.informationPanel.borderColor", "boolean", + "Set the border color of the 'information panel'" + }, + { + "sshapps.ui.informationPanel.borderThickness", "integer", + "Set the border thickness of the 'information panel'" + }, + { "sshapps.ui.toolBar", "boolean", "Enable / Disable the tool bar" }, + { "sshapps.ui.menuBar", "boolean", "Enable / Disable the menu bar" }, + { + "sshapps.ui.disabledActions", "string", + "Comma (,) separated list of disable actions" + }, + { "sshapps.ui.statusBar", "boolean", "Enable / Disable the menu bar" } + }; + + /** */ + protected Log log = LogFactory.getLog(SshToolsApplicationApplet.class); + + // Private instance variables + + /** */ + protected LoadingPanel loadingPanel; + + /** */ + protected JSeparator toolSeparator; + + /** */ + protected SshToolsApplicationPanel applicationPanel; + + /** */ + protected Color infoForeground; + + /** */ + protected int infoBorderThickness; + + /** */ + protected boolean toolBar; + + /** */ + protected boolean menuBar; + + /** */ + protected boolean statusBar; + + /** */ + protected Color infoBackground; + + /** */ + protected Color infoBorderColor; + + /** */ + protected String disabledActions; + + /** +* +* +* @param key +* @param def +* +* @return +*/ + public String getParameter(String key, String def) { + String v = getParameter(key); + + return (v != null) ? v : def; + } + + /** +* +*/ + public void init() { + try { + Runnable r = new Runnable() { + public void run() { + try { + getContentPane().setLayout(new BorderLayout()); + setAppletComponent(loadingPanel = new LoadingPanel()); + initApplet(); + + JComponent p = buildAppletComponent(); + startApplet(); + setAppletComponent(p); + } catch (Throwable t) { + seriousAppletError(t); + } + } + }; + + Thread t = new Thread(r); + t.start(); + } catch (Throwable t) { + seriousAppletError(t); + } + } + + /** +* +* +* @throws IOException +*/ + public void initApplet() throws IOException { + /*ConfigurationLoader.setLogfile(getParameter("sshapps.log.file", +"@console@")); + log.getRootLogger().setLevel(org.apache.log4j.Level.toLevel( +getParameter("sshapps.log.level", "DEBUG")));*/ + ConfigurationLoader.initialize(false); + infoBackground = PropertyUtil.stringToColor(getParameter( + "sshapps.ui.informationPanel.background", + PropertyUtil.colorToString(new Color(255, 255, 204)))); + infoForeground = PropertyUtil.stringToColor(getParameter( + "sshapps.ui.informationPanel.foreground", + PropertyUtil.colorToString(Color.black))); + infoBorderColor = PropertyUtil.stringToColor(getParameter( + "sshapps.ui.informationPanel.borderColor", + PropertyUtil.colorToString(Color.black))); + infoBorderThickness = PropertyUtil.stringToInt(getParameter( + "sshapps.ui.informationPanel.borderThickness", "1"), 1); + toolBar = getParameter("sshapps.ui.toolBar", "true").equalsIgnoreCase("true"); + menuBar = getParameter("sshapps.ui.menuBar", "true").equalsIgnoreCase("true"); + statusBar = getParameter("sshapps.ui.statusBar", "true") + .equalsIgnoreCase("true"); + disabledActions = getParameter("sshapps.ui.disabledActions", ""); + } + + /** +* +*/ + public void startApplet() { + } + + /** +* +* +* @return +* +* @throws IOException +* @throws SshToolsApplicationException +*/ + public JComponent buildAppletComponent() + throws IOException, SshToolsApplicationException { + loadingPanel.setStatus("Creating application"); + applicationPanel = createApplicationPanel(); + loadingPanel.setStatus("Building action components"); + applicationPanel.rebuildActionComponents(); + log.debug("Disabled actions list = " + disabledActions); + + StringTokenizer tk = new StringTokenizer((disabledActions == null) ? "" + : disabledActions, + ","); + + while (tk.hasMoreTokens()) { + String n = tk.nextToken(); + log.debug("Disable " + n); + applicationPanel.setActionVisible(n, false); + } + + JPanel p = new JPanel(new BorderLayout()); + JPanel n = new JPanel(new BorderLayout()); + + if (applicationPanel.getJMenuBar() != null) { + n.add(applicationPanel.getJMenuBar(), BorderLayout.NORTH); + log.debug("Setting menu bar visibility to " + menuBar); + applicationPanel.setMenuBarVisible(menuBar); + } + + if (applicationPanel.getToolBar() != null) { + JPanel t = new JPanel(new BorderLayout()); + t.add(applicationPanel.getToolBar(), BorderLayout.NORTH); + applicationPanel.setToolBarVisible(toolBar); + t.add(toolSeparator = new JSeparator(JSeparator.HORIZONTAL), + BorderLayout.SOUTH); + toolSeparator.setVisible(applicationPanel.getToolBar().isVisible()); + + final SshToolsApplicationPanel pnl = applicationPanel; + applicationPanel.getToolBar().addComponentListener(new ComponentAdapter() { + public void componentHidden(ComponentEvent evt) { + toolSeparator.setVisible(pnl.getToolBar().isVisible()); + } + }); + n.add(t, BorderLayout.SOUTH); + } + + p.add(n, BorderLayout.NORTH); + p.add(applicationPanel, BorderLayout.CENTER); + + if (applicationPanel.getStatusBar() != null) { + p.add(applicationPanel.getStatusBar(), BorderLayout.SOUTH); + applicationPanel.setStatusBarVisible(statusBar); + } + + return p; + } + + /** +* +* +* @param name +*/ + public void doAction(String name) { + StandardAction a = applicationPanel.getAction(name); + + if (a != null) { + if (a.isEnabled()) { + log.debug("Performing action " + a.getName()); + a.actionPerformed(new ActionEvent(this, + ActionEvent.ACTION_PERFORMED, a.getActionCommand())); + } else { + log.warn("No performing action '" + a.getName() + + "' because it is disabled."); + } + } else { + log.error("No action named " + name); + } + } + + /** +* +* +* @return +* +* @throws SshToolsApplicationException +*/ + public abstract SshToolsApplicationPanel createApplicationPanel() + throws SshToolsApplicationException; + + /** +* +* +* @param component +*/ + protected void setAppletComponent(JComponent component) { + if (getContentPane().getComponentCount() > 0) { + getContentPane().invalidate(); + getContentPane().removeAll(); + } + + getContentPane().add(component, BorderLayout.CENTER); + getContentPane().validate(); + getContentPane().repaint(); + } + + /** +* +* +* @param t +*/ + protected void seriousAppletError(Throwable t) { + StringBuffer buf = new StringBuffer(); + buf.append("<html><p>A serious error has occured ...</p><br>"); + buf.append("<p><font size=\"-1\" color=\"#ff0000\"><b>"); + + StringWriter writer = new StringWriter(); + t.printStackTrace(new PrintWriter(writer, true)); + + StringTokenizer tk = new StringTokenizer(writer.toString(), "\n"); + + while (tk.hasMoreTokens()) { + String msg = tk.nextToken(); + buf.append(msg); + + if (tk.hasMoreTokens()) { + buf.append("<br>"); + } + } + + buf.append("</b></font></p><html>"); + + SshToolsApplicationAppletPanel p = new SshToolsApplicationAppletPanel(); + p.setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(0, 0, 8, 0); + gbc.fill = GridBagConstraints.NONE; + UIUtil.jGridBagAdd(p, new JLabel(buf.toString()), gbc, + GridBagConstraints.REMAINDER); + setAppletComponent(p); + } + + /** +* +*/ + public void start() { + } + + /** +* +*/ + public void stop() { + } + + /** +* +*/ + public void destroy() { + } + + /** +* +* +* @return +*/ + public String[][] getParameterInfo() { + return PARAMETER_INFO; + } + + public class SshToolsApplicationAppletContainer extends JPanel + implements SshToolsApplicationContainer { + // Private instance variables + private SshToolsApplicationPanel panel; + private SshToolsApplication application; + + //Construct the applet + public SshToolsApplicationAppletContainer() { + } + + public void init(SshToolsApplication application, + SshToolsApplicationPanel panel) throws SshToolsApplicationException { + this.application = application; + this.panel = panel; + panel.registerActionMenu(new SshToolsApplicationPanel.ActionMenu( + "Help", "Help", 'h', 99)); + panel.registerAction(new AboutAction(this, application)); + getApplicationPanel().rebuildActionComponents(); + } + + public void setContainerTitle(String title) { + getAppletContext().showStatus(title); + } + + public SshToolsApplicationPanel getApplicationPanel() { + return panel; + } + + public void closeContainer() { + // We dont do anything here + } + + public void setContainerVisible(boolean visible) { + setVisible(visible); + } + + public boolean isContainerVisible() { + return isVisible(); + } + } + + class SshToolsApplicationAppletPanel extends JPanel { + SshToolsApplicationAppletPanel() { + super(); + setOpaque(true); + setBackground(infoBackground); + setForeground(infoForeground); + setBorder(BorderFactory.createLineBorder(infoBorderColor, + infoBorderThickness)); + } + } + + class LoadingPanel extends SshToolsApplicationAppletPanel { + private JProgressBar bar; + + LoadingPanel() { + super(); + setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(0, 0, 8, 0); + gbc.fill = GridBagConstraints.NONE; + UIUtil.jGridBagAdd(this, new JLabel("Loading " + getAppletInfo()), + gbc, GridBagConstraints.REMAINDER); + bar = new JProgressBar(0, 100); + + //bar.setIndeterminate(true); + bar.setStringPainted(true); + UIUtil.jGridBagAdd(this, bar, gbc, GridBagConstraints.REMAINDER); + } + + public void setStatus(String status) { + bar.setString(status); + } + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationClientApplet.java b/src/com/sshtools/common/ui/SshToolsApplicationClientApplet.java new file mode 100644 index 0000000000000000000000000000000000000000..d59bfc0a0c76cc04416cefa6f16d4e8eefa64ca7 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationClientApplet.java @@ -0,0 +1,248 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; +import com.sshtools.common.util.PropertyUtil; + +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClientFactory; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.IOUtil; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.MalformedURLException; +import java.net.URL; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public abstract class SshToolsApplicationClientApplet + extends SshToolsApplicationApplet { + /** */ + public final static String[][] CLIENT_PARAMETER_INFO = { + { + "sshapps.connection.url", "string", + "The URL of a connection profile to open" + }, + { "sshapps.connection.host", "string", "The host to connect to" }, + { "sshapps.connection.userName", "string", "The user to connect as" }, + { + "sshapps.connection.authenticationMethod", "string", + "Authentication method. password,publickey etc." + }, + { + "sshapps.connection.connectImmediately", "boolean", + "Connect immediately." + }, + { + "sshapps.connection.showConnectionDialog", "boolean", + "Show connection dialog." + }, + { + "sshapps.connection.disableHostKeyVerification", "boolean", + "Disable the host key verification dialog." + } + }; + + /** */ + protected Log log = LogFactory.getLog(SshToolsApplicationClientApplet.class); + + // Private instance variables + private String connectionProfileLocation; + + /** */ + protected String authenticationMethod; + + /** */ + protected String host; + + /** */ + protected int port; + + /** */ + protected String user; + + /** */ + protected boolean connectImmediately; + + /** */ + protected boolean showConnectionDialog; + + /** */ + protected boolean disableHostKeyVerification; + + /** */ + protected SshToolsConnectionProfile profile; + + /** +* +* +* @throws IOException +*/ + public void initApplet() throws IOException { + super.initApplet(); + connectionProfileLocation = getParameter("sshapps.connectionProfile.url", + ""); + + // Get the connection parameters + host = getParameter("sshapps.connection.host", ""); + port = PropertyUtil.stringToInt(getParameter( + "sshapps.connection.port", ""), 22); + user = getParameter("sshapps.connection.userName", + ConfigurationLoader.checkAndGetProperty("user.home", "")); + authenticationMethod = getParameter("sshapps.connection.authenticationMethod", + ""); + connectImmediately = getParameter("sshapps.connection.connectImmediately", + "false").equalsIgnoreCase("true"); + showConnectionDialog = getParameter("sshapps.connection.showConnectionDialog", + "false").equalsIgnoreCase("true"); + disableHostKeyVerification = getParameter("sshapps.connection.disableHostKeyVerification", + "false").equalsIgnoreCase("true"); + buildProfile(); + } + + /** +* +*/ + public void startApplet() { + // Disable the host key verification if requested + if (disableHostKeyVerification) { + ((SshToolsApplicationClientPanel) applicationPanel).setHostHostVerification(null); + ((SshToolsApplicationClientPanel) applicationPanel).application.removeAdditionalOptionsTab( + "Hosts"); + log.debug("Host key verification disabled"); + } else { + log.debug("Host key verification enabled"); + } + + if (connectImmediately) { + loadingPanel.setStatus("Connecting"); + + if (showConnectionDialog) { + SshToolsConnectionProfile newProfile = ((SshToolsApplicationClientPanel) applicationPanel).newConnectionProfile(profile); + + if (newProfile != null) { + profile = newProfile; + ((SshToolsApplicationClientPanel) applicationPanel).connect(profile, + true); + } + } else { + ((SshToolsApplicationClientPanel) applicationPanel).connect(profile, + false); + } + } + } + + /** +* +* +* @throws IOException +*/ + protected void buildProfile() throws IOException { + profile = new SshToolsConnectionProfile(); + + // Load the connection profile if specified + if (!connectionProfileLocation.equals("")) { + log.info("Loading connection profile " + connectionProfileLocation); + loadingPanel.setStatus("Loading connection profile"); + + InputStream in = null; + + try { + URL u = null; + + try { + u = new URL(connectionProfileLocation); + } catch (MalformedURLException murle) { + u = new URL(getCodeBase() + "/" + + connectionProfileLocation); + } + + log.info("Full URL of connection profile is " + u); + in = u.openStream(); + profile.open(in); + } finally { + IOUtil.closeStream(in); + } + } + + if (!host.equals("")) { + log.info("Building connection profile from parameters "); + log.debug("Setting host to " + host); + profile.setHost(host); + log.debug("Setting port to " + port); + profile.setPort(port); + log.debug("Setting username to " + user); + profile.setUsername(user); + + if (!authenticationMethod.equals("")) { + try { + log.debug("Adding authentication method " + + authenticationMethod); + + SshAuthenticationClient authClient = SshAuthenticationClientFactory.newInstance(authenticationMethod); + profile.addAuthenticationMethod(authClient); + } catch (Exception e) { + log.error("Could not add authentication method.", e); + } + } + } + } + + /** +* +*/ + public void destroy() { + if (applicationPanel.isConnected()) { + ((SshToolsApplicationClientPanel) applicationPanel).closeConnection(true); + } + } + + /** +* +* +* @return +*/ + public String[][] getParameterInfo() { + String[][] s = super.getParameterInfo(); + String[][] p = new String[s.length + CLIENT_PARAMETER_INFO.length][]; + System.arraycopy(s, 0, p, 0, s.length); + System.arraycopy(CLIENT_PARAMETER_INFO, 0, p, s.length, + CLIENT_PARAMETER_INFO.length); + + return p; + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationClientPanel.java b/src/com/sshtools/common/ui/SshToolsApplicationClientPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..ed69eb4cac9712afd5b8f3ea59346b45bea9f1cc --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationClientPanel.java @@ -0,0 +1,1012 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.authentication.AuthenticationDialog; +import com.sshtools.common.authentication.BannerDialog; +import com.sshtools.common.authentication.KBIRequestHandlerDialog; +import com.sshtools.common.authentication.PasswordAuthenticationDialog; +import com.sshtools.common.authentication.PasswordChange; +import com.sshtools.common.authentication.PublicKeyAuthenticationPrompt; +import com.sshtools.common.automate.RemoteIdentification; +import com.sshtools.common.automate.RemoteIdentificationException; +import com.sshtools.common.automate.RemoteIdentificationFactory; +import com.sshtools.common.configuration.InvalidProfileFileException; +import com.sshtools.common.configuration.SshToolsConnectionProfile; +import com.sshtools.common.hosts.DialogKnownHostsKeyVerification; + +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.SshException; +import com.sshtools.j2ssh.SshThread; +import com.sshtools.j2ssh.agent.AgentAuthenticationClient; +import com.sshtools.j2ssh.agent.AgentNotAvailableException; +import com.sshtools.j2ssh.agent.SshAgentClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.KBIAuthenticationClient; +import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient; +import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClientFactory; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.transport.AbstractKnownHostsKeyVerification; +import com.sshtools.j2ssh.transport.HostKeyVerification; +import com.sshtools.j2ssh.transport.InvalidHostFileException; +import com.sshtools.j2ssh.transport.TransportProtocolException; +import com.sshtools.j2ssh.transport.TransportProtocolState; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.Frame; +import java.awt.LayoutManager; + +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; + +import java.security.AccessControlException; +import java.security.AccessController; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.filechooser.FileFilter; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.24 $ + */ +public abstract class SshToolsApplicationClientPanel + extends SshToolsApplicationPanel { + /** */ + public final static String PREF_CONNECTION_FILE_DIRECTORY = "sshapps.connectionFile.directory"; + + // + + /** */ + public final static int BANNER_TIMEOUT = 2000; + + /** */ + protected static AbstractKnownHostsKeyVerification ver; + + // + + /** */ + protected Log log = LogFactory.getLog(SshToolsApplicationClientPanel.class); + + /** */ + protected HostKeyVerification hostKeyVerification; + + /** */ + protected File currentConnectionFile; + + /** */ + protected boolean needSave; + + /** */ + protected SshToolsConnectionProfile currentConnectionProfile; + + /** */ + protected javax.swing.filechooser.FileFilter connectionFileFilter = new ConnectionFileFilter(); + + /** */ + protected SshClient ssh; + + /** +* Creates a new SshToolsApplicationClientPanel object. +*/ + public SshToolsApplicationClientPanel() { + super(); + } + + /** +* Creates a new SshToolsApplicationClientPanel object. +* +* @param mgr +*/ + public SshToolsApplicationClientPanel(LayoutManager mgr) { + super(mgr); + } + + /** +* +* +* @return +*/ + public abstract SshToolsConnectionTab[] getAdditionalConnectionTabs(); + + /** +* +* +* @return +*/ + public HostKeyVerification getHostKeyVerification() { + return hostKeyVerification; + } + + /** +* +* +* @param hostKeyVerification +*/ + public void setHostHostVerification(HostKeyVerification hostKeyVerification) { + this.hostKeyVerification = hostKeyVerification; + } + + /** +* +* +* @param application +* +* @throws SshToolsApplicationException +*/ + public void init(SshToolsApplication application) + throws SshToolsApplicationException { + super.init(application); + + try { + //if (ver == null) { + ver = new DialogKnownHostsKeyVerification(this); + + //} + setHostHostVerification(ver); + + if (ver.isHostFileWriteable()) { + application.addAdditionalOptionsTab(new HostsTab(ver)); + } + } catch (InvalidHostFileException uhfe) { + log.warn("Host key verification will be DISABLED.", uhfe); + } + } + + /** +* +*/ + public void editConnection() { + // Create a file chooser with the current directory set to the + // application home + JFileChooser fileDialog = new JFileChooser(PreferencesStore.get( + PREF_CONNECTION_FILE_DIRECTORY, + System.getProperty("sshtools.home", + System.getProperty("user.home")))); + fileDialog.setFileFilter(connectionFileFilter); + + // Show it + int ret = fileDialog.showOpenDialog(this); + + // If we've approved the selection then process + if (ret == fileDialog.APPROVE_OPTION) { + PreferencesStore.put(PREF_CONNECTION_FILE_DIRECTORY, + fileDialog.getCurrentDirectory().getAbsolutePath()); + + // Get the file + File f = fileDialog.getSelectedFile(); + + // Load the profile + SshToolsConnectionProfile p = new SshToolsConnectionProfile(); + + try { + p.open(f); + + if (editConnection(p)) { + saveConnection(false, f, p); + } + } catch (IOException ioe) { + showErrorMessage(this, "Failed to load connection profile.", + "Error", ioe); + } + } + } + + /** +* +* +* @param profile +* +* @return +*/ + public SshToolsConnectionProfile newConnectionProfile( + SshToolsConnectionProfile profile) { + return SshToolsConnectionPanel.showConnectionDialog(SshToolsApplicationClientPanel.this, + profile, getAdditionalConnectionTabs()); + } + + /** +* +*/ + public void open() { + // Create a file chooser with the current directory set to the + // application home + String prefsDir = super.getApplication() + .getApplicationPreferencesDirectory() + .getAbsolutePath(); + JFileChooser fileDialog = new JFileChooser(prefsDir); + fileDialog.setFileFilter(connectionFileFilter); + + // Show it + int ret = fileDialog.showOpenDialog(this); + + // If we've approved the selection then process + if (ret == fileDialog.APPROVE_OPTION) { + PreferencesStore.put(PREF_CONNECTION_FILE_DIRECTORY, + fileDialog.getCurrentDirectory().getAbsolutePath()); + + // Get the file + File f = fileDialog.getSelectedFile(); + open(f); + } + } + + /** +* +* +* @param f +*/ + public void open(File f) { + log.debug("Opening connection file " + f); + + // Make sure a connection is not already open + if (isConnected()) { + Option optNew = new Option("New", "New create a new window", 'n'); + Option optClose = new Option("Close", "Close current connection", + 'l'); + Option optCancel = new Option("Cancel", + "Cancel the opening of this connection", 'c'); + OptionsDialog dialog = OptionsDialog.createOptionDialog(this, + new Option[] { optNew, optClose, optCancel }, + "You already have a connection open. Select\n" + + "Close to close the current connection, New\n" + + "to create a new terminal or Cancel to abort.", + "Existing connection", optNew, null, + UIManager.getIcon("OptionPane.warningIcon")); + UIUtil.positionComponent(SwingConstants.CENTER, dialog); + dialog.setVisible(true); + + Option opt = dialog.getSelectedOption(); + + if ((opt == null) || (opt == optCancel)) { + return; + } else if (opt == optNew) { + try { + SshToolsApplicationContainer c = (SshToolsApplicationContainer) application.newContainer(); + ((SshToolsApplicationClientPanel) c.getApplicationPanel()).open(f); + + return; + } catch (SshToolsApplicationException stae) { + log.error(stae); + } + } else { + closeConnection(true); + } + } + + // Save to MRU + if (getApplication().getMRUModel() != null) { + getApplication().getMRUModel().add(f); + } + + // Make sure its not invalid + if (f != null) { + // Create a new connection properties object + SshToolsConnectionProfile profile = new SshToolsConnectionProfile(); + + try { + // Open the file + profile.open(f.getAbsolutePath()); + setNeedSave(false); + currentConnectionFile = f; + setContainerTitle(f); + + // Connect with the new details. + connect(profile, false); + } catch (InvalidProfileFileException fnfe) { + showExceptionMessage(fnfe.getMessage(), "Open Connection"); + } catch (SshException e) { + e.printStackTrace(); + showExceptionMessage("An unexpected error occured!", + "Open Connection"); + } + } else { + showExceptionMessage("Invalid file specified", "Open Connection"); + } + } + + /** +* +* +* @param profile +* @param newProfile +*/ + public void connect(final SshToolsConnectionProfile profile, + final boolean newProfile) { + // We need to connect + ssh = new SshClient(); + + // Set the current connection properties + setCurrentConnectionProfile(profile); + + // We'll do the threading rather than j2ssh as we want to get errors + Runnable r = new Runnable() { + public void run() { + // Update the status bar + getStatusBar().setStatusText("Connecting"); + getStatusBar().setHost(getCurrentConnectionProfile() + .getHost(), + getCurrentConnectionProfile().getPort()); + getStatusBar().setUser(getCurrentConnectionProfile() + .getUsername()); + + // + try { + log.info("Connecting to " + + getCurrentConnectionProfile().getHost() + " as " + + getCurrentConnectionProfile().getUsername()); + ssh.connect(getCurrentConnectionProfile(), + (getHostKeyVerification() == null) + ? new SinkHostKeyVerification() + : getHostKeyVerification()); + + // Set the remote id if we can find on + try { + RemoteIdentification rid = RemoteIdentificationFactory.getInstance(ssh.getServerId(), + ssh.getConnectionProperties().getHost()); + getStatusBar().setRemoteId(rid.getName( + ssh.getServerId())); + } catch (RemoteIdentificationException ex) { + getStatusBar().setRemoteId("Unknown"); + } + + if (postConnection()) { + if (!authenticateUser(newProfile)) { + closeConnection(false); + } else { + setAvailableActions(); + } + } + } catch (IOException sshe) { + ssh = null; + showExceptionMessage("Connection Error", + "Could not establish a connection to host: \n\n " + + sshe.getMessage()); + SshToolsApplicationClientPanel.this.closeConnection(false); + } catch (SecurityException se) { + ssh = null; + showErrorMessage(SshToolsApplicationClientPanel.this, + "Error", se); + SshToolsApplicationClientPanel.this.closeConnection(false); + } + } + }; + + Thread thread = new SshThread(r, + application.getApplicationName() + " connection", true); + thread.start(); + } + + /** +* +* +* @param ssh +* @param profile +* +* @throws IOException +*/ + public void connect(SshClient ssh, SshToolsConnectionProfile profile) + throws IOException { + this.ssh = ssh; + + if (!ssh.isAuthenticated()) { + authenticateUser(false); + } + + // Set the current connection properties + setCurrentConnectionProfile(profile); + authenticationComplete(false); + } + + /** +* +* +* @param newProfile +* +* @return +* +* @throws IOException +*/ + protected boolean authenticateUser(boolean newProfile) + throws IOException { + // We should now authenticate + int result = AuthenticationProtocolState.READY; + + // Our authenticated flag + boolean authenticated = false; + + // Get the supported authentication methods + java.util.List supported = SshAuthenticationClientFactory.getSupportedMethods(); + + // If the server supports public key lets look for an agent and try + // some of his keys + if (supported.contains("publickey")) { + if (System.getProperty("sshtools.agent") != null) { + try { + SshAgentClient agent = SshAgentClient.connectLocalAgent("SSHTerm", + System.getProperty("sshtools.agent") /*, 5*/); + AgentAuthenticationClient aac = new AgentAuthenticationClient(); + aac.setAgent(agent); + aac.setUsername(getCurrentConnectionProfile().getUsername()); + result = ssh.authenticate(aac); + agent.close(); + } catch (AgentNotAvailableException ex) { + log.info("No agent was available for authentication"); + + // Just continue + } + + if (result == AuthenticationProtocolState.COMPLETE) { + authenticationComplete(newProfile); + + return true; + } + } + } + + // Create a list for display that will contain only the + // supported and available methods + java.util.List display = new java.util.ArrayList(); + + // Get the available methods + java.util.List auths = null; + auths = ssh.getAvailableAuthMethods(getCurrentConnectionProfile() + .getUsername()); + + // Did we receive a banner from the remote computer + final String banner = ssh.getAuthenticationBanner(BANNER_TIMEOUT); + + if (banner != null) { + if (!banner.trim().equals("")) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + BannerDialog.showBannerDialog(SshToolsApplicationClientPanel.this, + banner); + } + }); + } catch (Exception e) { + log.error("Failed to invoke and wait on BannerDialog", e); + } + } + } + + // Are there any authentication methods within the properties file? + // Iterate through selecting only the supported and available + Iterator it = supported.iterator(); + + while (it.hasNext() && !authenticated) { + Object obj = it.next(); + + if (auths.contains(obj)) { + display.add(obj); + } + } + + // First look to see if we have any authenticaiton methods available + // in the profile properties object as this will overide a manual selection + java.util.Map authMethods = (Map) ((HashMap) getCurrentConnectionProfile() + .getAuthenticationMethods()).clone(); + it = authMethods.entrySet().iterator(); + + //Iterator it2 = null; + java.util.List selected; + + // Loop until the user either cancels or completes + boolean completed = false; + SshAuthenticationClient auth; + Map.Entry entry; + String msg = null; + + while (!completed && + (ssh.getConnectionState().getValue() != TransportProtocolState.DISCONNECTED)) { + auth = null; + + // Select an authentication method from the properties file or + // prompt the user to choose + if (it.hasNext()) { + Object obj = it.next(); + + if (obj instanceof Map.Entry) { + entry = (Map.Entry) obj; + auth = (SshAuthenticationClient) entry.getValue(); + } else if (obj instanceof String) { + auth = SshAuthenticationClientFactory.newInstance((String) obj); + auth.setUsername(getCurrentConnectionProfile().getUsername()); + } else { + closeConnection(true); + throw new IOException( + "Iterator of Map or List of String expected"); + } + } else { + selected = AuthenticationDialog.showAuthenticationDialog(this, + display, ((msg == null) ? "" : msg)); + + if (selected.size() > 0) { + it = selected.iterator(); + } else { + closeConnection(true); + + return false; + } + } + + if (auth != null) { + // The password authentication client can act upon requests to change the password + + /* if (auth instanceof PasswordAuthenticationClient) { +PasswordAuthenticationDialog dialog = new PasswordAuthenticationDialog(SshTerminalPanel.this); +((PasswordAuthenticationClient) auth).setAuthenticationPrompt(dialog); +( (PasswordAuthenticationClient) auth) +.setPasswordChangePrompt(PasswordChange.getInstance()); +PasswordChange.getInstance().setParentComponent( +SshTerminalPanel.this); +}*/ + + // Show the implementations dialog + // if(auth.showAuthenticationDialog()) { + // Authentication with the details supplied + result = showAuthenticationPrompt(auth); //ssh.authenticate(auth); + + if (result == AuthenticationProtocolState.FAILED) { + msg = auth.getMethodName() + + " authentication failed, try again?"; + } + + // If the result returned partial success then continue + if (result == AuthenticationProtocolState.PARTIAL) { + // We succeeded so add to the connections authenticaiton + // list and continue on to the next one + getCurrentConnectionProfile().addAuthenticationMethod(auth); + msg = auth.getMethodName() + + " authentication succeeded but another is required"; + } + + if (result == AuthenticationProtocolState.COMPLETE) { + authenticated = true; + + //If successfull add to the connections list so we can save later + getCurrentConnectionProfile().addAuthenticationMethod(auth); + + // Set the completed flag + completed = true; + authenticationComplete(newProfile); + } + + if (result == AuthenticationProtocolState.CANCELLED) { + ssh.disconnect(); + + return false; + } + + // } + // else { + // User has cancelled the authenticaiton + // closeConnection(true); + // return false; + // } + } + + // end of if + } + + // end of while + return authenticated; + } + + /** +* +* +* @param instance +* +* @return +* +* @throws IOException +*/ + protected int showAuthenticationPrompt(SshAuthenticationClient instance) + throws IOException { + instance.setUsername(getCurrentConnectionProfile().getUsername()); + + if (instance instanceof PasswordAuthenticationClient) { + PasswordAuthenticationDialog dialog = new PasswordAuthenticationDialog((Frame) SwingUtilities.getAncestorOfClass( + Frame.class, SshToolsApplicationClientPanel.this)); + instance.setAuthenticationPrompt(dialog); + ((PasswordAuthenticationClient) instance).setPasswordChangePrompt(PasswordChange.getInstance()); + PasswordChange.getInstance().setParentComponent(SshToolsApplicationClientPanel.this); + } else if (instance instanceof PublicKeyAuthenticationClient) { + PublicKeyAuthenticationPrompt prompt = new PublicKeyAuthenticationPrompt(SshToolsApplicationClientPanel.this); + instance.setAuthenticationPrompt(prompt); + } else if (instance instanceof KBIAuthenticationClient) { + KBIAuthenticationClient kbi = new KBIAuthenticationClient(); + ((KBIAuthenticationClient) instance).setKBIRequestHandler(new KBIRequestHandlerDialog( + (Frame) SwingUtilities.getAncestorOfClass(Frame.class, + SshToolsApplicationClientPanel.this))); + } + + return ssh.authenticate(instance); + } + + /** +* +* +* @return +*/ + public abstract boolean postConnection(); + + /** +* +* +* @param newProfile +* +* @throws SshException +* @throws IOException +*/ + public abstract void authenticationComplete(boolean newProfile) + throws SshException, IOException; + + /** +* +* +* @param file +*/ + public void setContainerTitle(File file) { + String verString = ConfigurationLoader.getVersionString(application.getApplicationName(), + application.getApplicationVersion()); + + if (container != null) { + container.setContainerTitle((file == null) ? verString + : (verString + " [" + + file.getName() + "]")); + } + } + + /** +* +* +* @param needSave +*/ + public void setNeedSave(boolean needSave) { + if (needSave != this.needSave) { + this.needSave = needSave; + setAvailableActions(); + } + } + + /** +* +* +* @param file +*/ + public void setCurrentConnectionFile(File file) { + currentConnectionFile = file; + } + + /** +* +* +* @return +*/ + public File getCurrentConnectionFile() { + return currentConnectionFile; + } + + /** +* +* +* @param profile +*/ + public void setCurrentConnectionProfile(SshToolsConnectionProfile profile) { + currentConnectionProfile = profile; + } + + /** +* +* +* @return +*/ + public SshToolsConnectionProfile getCurrentConnectionProfile() { + return currentConnectionProfile; + } + + /** +* +* +* @return +*/ + public boolean isNeedSave() { + return needSave; + } + + /** +* +* +* @return +*/ + public boolean isConnected() { + return (ssh != null) && ssh.isConnected(); + } + + /** +* +* +* @throws SshException +*/ + public void connect() throws SshException { + if (getCurrentConnectionProfile() == null) { + throw new SshException( + "Can't connect, no connection profile have been set."); + } + + // There isn't anywhere to store this setting yet + connect(getCurrentConnectionProfile(), false); + } + + /** +* +* +* @param disconnect +*/ + public void closeConnection(boolean disconnect) { + // + if (isNeedSave()) { + // Only allow saving of files if allowed by the security manager + try { + if (System.getSecurityManager() != null) { + AccessController.checkPermission(new FilePermission( + "<<ALL FILES>>", "write")); + + if (JOptionPane.showConfirmDialog(this, + "You have unsaved changes to the connection " + + ((currentConnectionFile == null) ? "<Untitled>" + : currentConnectionFile.getName()) + + ".\nDo you want to save the changes now?", + "Unsaved changes", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + saveConnection(false, getCurrentConnectionFile(), + getCurrentConnectionProfile()); + setNeedSave(false); + } + } + } catch (AccessControlException ace) { + log.warn( + "Changes made to connection, but security manager won't allow saving of files."); + } + } + + setCurrentConnectionFile(null); + } + + /** +* +* +* @return +*/ + protected boolean allowConnectionSettingsEditing() { + return true; + } + + /** +* +* +* @param profile +* +* @return +*/ + public boolean editConnection(SshToolsConnectionProfile profile) { + final SshToolsConnectionPanel panel = new SshToolsConnectionPanel(allowConnectionSettingsEditing()); + SshToolsConnectionTab[] tabs = getAdditionalConnectionTabs(); + + for (int i = 0; (tabs != null) && (i < tabs.length); i++) { + tabs[i].setConnectionProfile(profile); + panel.addTab(tabs[i]); + } + + panel.setConnectionProfile(profile); + + final Option ok = new Option("Ok", + "Apply the settings and close this dialog", 'o'); + final Option cancel = new Option("Cancel", + "Close this dialog without applying the settings", 'c'); + OptionCallback callback = new OptionCallback() { + public boolean canClose(OptionsDialog dialog, Option option) { + if (option == ok) { + return panel.validateTabs(); + } + + return true; + } + }; + + OptionsDialog od = OptionsDialog.createOptionDialog(SshToolsApplicationClientPanel.this, + new Option[] { ok, cancel }, panel, "Connection Settings", ok, + callback, null); + od.pack(); + UIUtil.positionComponent(SwingConstants.CENTER, od); + od.setVisible(true); + + if (od.getSelectedOption() == ok) { + panel.applyTabs(); + + if (profile == getCurrentConnectionProfile()) { + setNeedSave(true); + } + + return true; + } + + return false; + } + + /** +* +* +* @param saveAs +* @param file +* @param profile +* +* @return +*/ + public File saveConnection(boolean saveAs, File file, + SshToolsConnectionProfile profile) { + if (profile != null) { + if ((file == null) || saveAs) { + String prefsDir = super.getApplication() + .getApplicationPreferencesDirectory() + .getAbsolutePath(); + JFileChooser fileDialog = new JFileChooser(prefsDir); + fileDialog.setFileFilter(connectionFileFilter); + + int ret = fileDialog.showSaveDialog(this); + + if (ret == fileDialog.CANCEL_OPTION) { + return null; + } + + file = fileDialog.getSelectedFile(); + + if (!file.getName().toLowerCase().endsWith(".xml")) { + file = new File(file.getAbsolutePath() + ".xml"); + } + } + + try { + if (saveAs && file.exists()) { + if (JOptionPane.showConfirmDialog(this, + "File already exists. Are you sure?", + "File exists", JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE) == JOptionPane.NO_OPTION) { + return null; + } + } + + // Check to make sure its valid + if (file != null) { + // Save the connection details to file + log.debug("Saving connection to " + file.getAbsolutePath()); + profile.save(file.getAbsolutePath()); + + if (profile == getCurrentConnectionProfile()) { + log.debug( + "Current connection saved, disabling save action."); + setNeedSave(false); + } + + return file; + } else { + showExceptionMessage("The file specified is invalid!", + "Save Connection"); + } + } catch (InvalidProfileFileException e) { + showExceptionMessage(e.getMessage(), "Save Connection"); + } + } + + return null; + } + + public static class ActionMenu implements Comparable { + int weight; + int mnemonic; + String name; + String displayName; + + public ActionMenu(String name, String displayName, int mnemonic, + int weight) { + this.name = name; + this.displayName = displayName; + this.mnemonic = mnemonic; + this.weight = weight; + } + + public int compareTo(Object o) { + int i = new Integer(weight).compareTo(new Integer( + ((ActionMenu) o).weight)); + + return (i == 0) + ? displayName.compareTo(((ActionMenu) o).displayName) : i; + } + } + + class ToolBarActionComparator implements Comparator { + public int compare(Object o1, Object o2) { + int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.TOOLBAR_GROUP)); + + return (i == 0) + ? ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.TOOLBAR_WEIGHT)) : i; + } + } + + class MenuItemActionComparator implements Comparator { + public int compare(Object o1, Object o2) { + int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.MENU_ITEM_GROUP)); + + return (i == 0) + ? ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.MENU_ITEM_WEIGHT)) : i; + } + } + + class ConnectionFileFilter extends javax.swing.filechooser.FileFilter { + public boolean accept(File f) { + return f.isDirectory() || + f.getName().toLowerCase().endsWith(".xml"); + } + + public String getDescription() { + return "Connection files (*.xml)"; + } + } + + class SinkHostKeyVerification implements HostKeyVerification { + public boolean verifyHost(String host, SshPublicKey pk) + throws TransportProtocolException { + log.warn("Accepting host " + host + + " as host key verification is disabled."); + + return true; + } + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationContainer.java b/src/com/sshtools/common/ui/SshToolsApplicationContainer.java new file mode 100644 index 0000000000000000000000000000000000000000..22c50d577d1e0af7e025c2bad185164818817306 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationContainer.java @@ -0,0 +1,79 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface SshToolsApplicationContainer { + /** +* +* +* @param application +* @param panel +* +* @throws SshToolsApplicationException +*/ + public void init(SshToolsApplication application, + SshToolsApplicationPanel panel) throws SshToolsApplicationException; + + /** +* +* +* @return +*/ + public SshToolsApplicationPanel getApplicationPanel(); + + /** +* +*/ + public void closeContainer(); + + /** +* +* +* @param visible +*/ + public void setContainerVisible(boolean visible); + + /** +* +* +* @param title +*/ + public void setContainerTitle(String title); + + /** +* +* +* @return +*/ + public boolean isContainerVisible(); +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationException.java b/src/com/sshtools/common/ui/SshToolsApplicationException.java new file mode 100644 index 0000000000000000000000000000000000000000..247d7ab6edea228ef22e908bf06490cf480b2ae3 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationException.java @@ -0,0 +1,81 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.lang.reflect.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class SshToolsApplicationException extends Exception { + /** +* Creates a new SshToolsApplicationException object. +*/ + public SshToolsApplicationException() { + this(null, null); + } + + /** +* Creates a new SshToolsApplicationException object. +* +* @param msg +*/ + public SshToolsApplicationException(String msg) { + this(msg, null); + } + + /** +* Creates a new SshToolsApplicationException object. +* +* @param cause +*/ + public SshToolsApplicationException(Throwable cause) { + this(null, cause); + } + + /** +* Creates a new SshToolsApplicationException object. +* +* @param msg +* @param cause +*/ + public SshToolsApplicationException(String msg, Throwable cause) { + super(msg); + + if (cause != null) { + try { + Method m = getClass().getMethod("initCause", + new Class[] { Throwable.class }); + m.invoke(this, new Object[] { cause }); + } catch (Exception e) { + } + } + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationFrame.java b/src/com/sshtools/common/ui/SshToolsApplicationFrame.java new file mode 100644 index 0000000000000000000000000000000000000000..0bd4491f20f23f1e8bb54351b2ee2bd6e187b1da --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationFrame.java @@ -0,0 +1,303 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.SwingConstants; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class SshToolsApplicationFrame extends JFrame + implements SshToolsApplicationContainer { + // Preference names + + /** */ + public final static String PREF_LAST_FRAME_GEOMETRY = "application.lastFrameGeometry"; + + /** */ + protected Log log = LogFactory.getLog(SshToolsApplicationFrame.class); + + /** */ + protected StandardAction exitAction; + + /** */ + protected StandardAction aboutAction; + + /** */ + protected StandardAction newWindowAction; + + /** */ + protected JSeparator toolSeparator; + + // + private SshToolsApplicationPanel panel; + private SshToolsApplication application; + private boolean showAboutBox = true; + private boolean showExitAction = true; + private boolean showNewWindowAction = true; + private boolean showMenu = true; + + public void showAboutBox(boolean showAboutBox) { + this.showAboutBox = showAboutBox; + } + + public void showExitAction(boolean showExitAction) { + this.showExitAction = showExitAction; + } + + public void showNewWindowAction(boolean showNewWindowAction) { + this.showNewWindowAction = showNewWindowAction; + } + + /** +* +* +* @param application +* @param panel +* +* @throws SshToolsApplicationException +*/ + public void init(final SshToolsApplication application, + SshToolsApplicationPanel panel) throws SshToolsApplicationException { + this.panel = panel; + this.application = application; + + if (application != null) { + setTitle(ConfigurationLoader.getVersionString( + application.getApplicationName(), + application.getApplicationVersion())); // + " " + application.getApplicationVersion()); + } + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + // Register the File menu + panel.registerActionMenu(new SshToolsApplicationPanel.ActionMenu( + "File", "File", 'f', 0)); + + // Register the Exit action + if (showExitAction && (application != null)) { + panel.registerAction(exitAction = new ExitAction(application, this)); + + // Register the New Window Action + } + + if (showNewWindowAction && (application != null)) { + panel.registerAction(newWindowAction = new NewWindowAction( + application)); + + // Register the Help menu + } + + panel.registerActionMenu(new SshToolsApplicationPanel.ActionMenu( + "Help", "Help", 'h', 99)); + + // Register the About box action + if (showAboutBox && (application != null)) { + panel.registerAction(aboutAction = new AboutAction(this, application)); + } + + getApplicationPanel().rebuildActionComponents(); + + JPanel p = new JPanel(new BorderLayout()); + + if (panel.getJMenuBar() != null) { + setJMenuBar(panel.getJMenuBar()); + } + + if (panel.getToolBar() != null) { + JPanel t = new JPanel(new BorderLayout()); + t.add(panel.getToolBar(), BorderLayout.NORTH); + t.add(toolSeparator = new JSeparator(JSeparator.HORIZONTAL), + BorderLayout.SOUTH); + toolSeparator.setVisible(panel.getToolBar().isVisible()); + + final SshToolsApplicationPanel pnl = panel; + panel.getToolBar().addComponentListener(new ComponentAdapter() { + public void componentHidden(ComponentEvent evt) { + log.debug("Tool separator is now " + + pnl.getToolBar().isVisible()); + toolSeparator.setVisible(pnl.getToolBar().isVisible()); + } + }); + p.add(t, BorderLayout.NORTH); + } + + p.add(panel, BorderLayout.CENTER); + + if (panel.getStatusBar() != null) { + p.add(panel.getStatusBar(), BorderLayout.SOUTH); + } + + getContentPane().setLayout(new GridLayout(1, 1)); + getContentPane().add(p); + + // Watch for the frame closing + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + if (application != null) { + application.closeContainer(SshToolsApplicationFrame.this); + } else { + int confirm = JOptionPane.showOptionDialog(SshToolsApplicationFrame.this, + "Close " + getTitle() + "?", "Close Operation", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, null, null, null); + + if (confirm == 0) { + hide(); + } + } + } + }); + + // If this is the first frame, center the window on the screen + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + boolean found = false; + + if ((application != null) && (application.getContainerCount() != 0)) { + for (int i = 0; (i < application.getContainerCount()) && !found; + i++) { + SshToolsApplicationContainer c = application.getContainerAt(i); + + if (c instanceof SshToolsApplicationFrame) { + SshToolsApplicationFrame f = (SshToolsApplicationFrame) c; + setSize(f.getSize()); + + Point newLocation = new Point(f.getX(), f.getY()); + newLocation.x += 48; + newLocation.y += 48; + + if (newLocation.x > (screenSize.getWidth() - 64)) { + newLocation.x = 0; + } + + if (newLocation.y > (screenSize.getHeight() - 64)) { + newLocation.y = 0; + } + + setLocation(newLocation); + found = true; + } + } + } + + if (!found) { + // Is there a previous stored geometry we can use? + if (PreferencesStore.preferenceExists(PREF_LAST_FRAME_GEOMETRY)) { + setBounds(PreferencesStore.getRectangle( + PREF_LAST_FRAME_GEOMETRY, getBounds())); + } else { + pack(); + UIUtil.positionComponent(SwingConstants.CENTER, this); + } + } + } + + /** +* +* +* @param title +*/ + public void setContainerTitle(String title) { + setTitle(title); + } + + /** +* +* +* @return +*/ + public SshToolsApplication getApplication() { + return application; + } + + /** +* +* +* @param visible +*/ + public void setContainerVisible(boolean visible) { + setVisible(visible); + } + + /** +* +* +* @return +*/ + public boolean isContainerVisible() { + return isVisible(); + } + + /** +* +* +* @return +*/ + public SshToolsApplicationPanel getApplicationPanel() { + return panel; + } + + /** +* +*/ + public void closeContainer() { + /* If this is the last frame to close, then store its geometry for use +when the next frame opens */ + if ((application != null) && (application.getContainerCount() == 1)) { + PreferencesStore.putRectangle(PREF_LAST_FRAME_GEOMETRY, getBounds()); + } + + dispose(); + getApplicationPanel().deregisterAction(newWindowAction); + getApplicationPanel().deregisterAction(exitAction); + getApplicationPanel().deregisterAction(aboutAction); + getApplicationPanel().rebuildActionComponents(); + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationInternalFrame.java b/src/com/sshtools/common/ui/SshToolsApplicationInternalFrame.java new file mode 100644 index 0000000000000000000000000000000000000000..a27966a07031c0ba3b5a81a413890fbfc94bbd23 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationInternalFrame.java @@ -0,0 +1,313 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSeparator; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.4 $ + */ +public class SshToolsApplicationInternalFrame extends JInternalFrame + implements SshToolsApplicationContainer { + // Preference names + + /** */ + public final static String PREF_LAST_FRAME_GEOMETRY = "application.lastFrameGeometry"; + + /** */ + protected Log log = LogFactory.getLog(SshToolsApplicationInternalFrame.class); + + /** */ + protected StandardAction exitAction; + + /** */ + protected StandardAction aboutAction; + + /** */ + protected StandardAction newWindowAction; + + /** */ + protected JSeparator toolSeparator; + + // + private SshToolsApplicationPanel panel; + private SshToolsApplication application; + private boolean showAboutBox = true; + private boolean showExitAction = true; + private boolean showNewWindowAction = true; + private boolean showMenu = true; + + public void showAboutBox(boolean showAboutBox) { + this.showAboutBox = showAboutBox; + } + + public void showExitAction(boolean showExitAction) { + this.showExitAction = showExitAction; + } + + public void showNewWindowAction(boolean showNewWindowAction) { + this.showNewWindowAction = showNewWindowAction; + } + + /** +* +* +* @param application +* @param panel +* +* @throws SshToolsApplicationException +*/ + public void init(final SshToolsApplication application, + SshToolsApplicationPanel panel) throws SshToolsApplicationException { + this.panel = panel; + this.application = application; + + if (application != null) { + setTitle(ConfigurationLoader.getVersionString( + application.getApplicationName(), + application.getApplicationVersion())); // + " " + application.getApplicationVersion()); + } + + // Register the File menu + panel.registerActionMenu(new SshToolsApplicationPanel.ActionMenu( + "File", "File", 'f', 0)); + + // Register the Exit action + if (showExitAction && (application != null)) { + panel.registerAction(exitAction = new ExitAction(application, this)); + + // Register the New Window Action + } + + if (showNewWindowAction && (application != null)) { + panel.registerAction(newWindowAction = new NewWindowAction( + application)); + + // Register the Help menu + } + + panel.registerActionMenu(new SshToolsApplicationPanel.ActionMenu( + "Help", "Help", 'h', 99)); + + // Register the About box action + if (showAboutBox && (application != null)) { + panel.registerAction(aboutAction = new AboutAction(this, application)); + } + + getApplicationPanel().rebuildActionComponents(); + + JPanel p = new JPanel(new BorderLayout()); + + if (panel.getJMenuBar() != null) { + setJMenuBar(panel.getJMenuBar()); + } + + if (panel.getToolBar() != null) { + JPanel t = new JPanel(new BorderLayout()); + t.add(panel.getToolBar(), BorderLayout.NORTH); + t.add(toolSeparator = new JSeparator(JSeparator.HORIZONTAL), + BorderLayout.SOUTH); + toolSeparator.setVisible(panel.getToolBar().isVisible()); + + final SshToolsApplicationPanel pnl = panel; + panel.getToolBar().addComponentListener(new ComponentAdapter() { + public void componentHidden(ComponentEvent evt) { + log.debug("Tool separator is now " + + pnl.getToolBar().isVisible()); + toolSeparator.setVisible(pnl.getToolBar().isVisible()); + } + }); + p.add(t, BorderLayout.NORTH); + } + + p.add(panel, BorderLayout.CENTER); + + if (panel.getStatusBar() != null) { + p.add(panel.getStatusBar(), BorderLayout.SOUTH); + } + + getContentPane().setLayout(new GridLayout(1, 1)); + getContentPane().add(p); + + // Watch for the frame closing + //setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); + addVetoableChangeListener(new VetoableChangeListener() { + public void vetoableChange(PropertyChangeEvent evt) + throws PropertyVetoException { + if (application != null) { + application.closeContainer(SshToolsApplicationInternalFrame.this); + } else { + if (evt.getPropertyName().equals(IS_CLOSED_PROPERTY)) { + boolean changed = ((Boolean) evt.getNewValue()).booleanValue(); + + if (changed) { + int confirm = JOptionPane.showOptionDialog(SshToolsApplicationInternalFrame.this, + "Close " + getTitle() + "?", + "Close Operation", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, null, + null, null); + + if (confirm == 0) { + SshToolsApplicationInternalFrame.this.getDesktopPane() + .remove(SshToolsApplicationInternalFrame.this); + } + } + } + } + } + }); + + /*this.addWindowListener(new WindowAdapter() { +public void windowClosing(WindowEvent evt) { +if(application!=null) +application.closeContainer(SshToolsApplicationFrame.this); +else +hide(); +} +}); +// If this is the first frame, center the window on the screen +/*Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); +boolean found = false; +if (application!=null && application.getContainerCount() != 0) { +for (int i = 0; (i < application.getContainerCount()) && !found; +i++) { +SshToolsApplicationContainer c = application.getContainerAt(i); +if (c instanceof SshToolsApplicationFrame) { +SshToolsApplicationFrame f = (SshToolsApplicationFrame) c; +setSize(f.getSize()); +Point newLocation = new Point(f.getX(), f.getY()); +newLocation.x += 48; +newLocation.y += 48; +if (newLocation.x > (screenSize.getWidth() - 64)) { + newLocation.x = 0; +} +if (newLocation.y > (screenSize.getHeight() - 64)) { + newLocation.y = 0; +} +setLocation(newLocation); +found = true; +} +} +} +if (!found) { +// Is there a previous stored geometry we can use? +if (PreferencesStore.preferenceExists(PREF_LAST_FRAME_GEOMETRY)) { +setBounds(PreferencesStore.getRectangle( + PREF_LAST_FRAME_GEOMETRY, getBounds())); +} +else { +pack(); +UIUtil.positionComponent(SwingConstants.CENTER, this); +} +}*/ + pack(); + } + + /** +* +* +* @param title +*/ + public void setContainerTitle(String title) { + setTitle(title); + } + + /** +* +* +* @return +*/ + public SshToolsApplication getApplication() { + return application; + } + + /** +* +* +* @param visible +*/ + public void setContainerVisible(boolean visible) { + setVisible(visible); + } + + /** +* +* +* @return +*/ + public boolean isContainerVisible() { + return isVisible(); + } + + /** +* +* +* @return +*/ + public SshToolsApplicationPanel getApplicationPanel() { + return panel; + } + + /** +* +*/ + public void closeContainer() { + /* If this is the last frame to close, then store its geometry for use +when the next frame opens */ + if ((application != null) && (application.getContainerCount() == 1)) { + PreferencesStore.putRectangle(PREF_LAST_FRAME_GEOMETRY, getBounds()); + } + + dispose(); + getApplicationPanel().deregisterAction(newWindowAction); + getApplicationPanel().deregisterAction(exitAction); + getApplicationPanel().deregisterAction(aboutAction); + getApplicationPanel().rebuildActionComponents(); + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationPanel.java b/src/com/sshtools/common/ui/SshToolsApplicationPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..a16b1938eaa0eefadf4d5b5a21404c28bf4e20d0 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationPanel.java @@ -0,0 +1,794 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.Component; +import java.awt.LayoutManager; +import java.awt.event.ActionEvent; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Vector; + +import javax.swing.Action; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JToolBar; +import javax.swing.filechooser.FileFilter; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.23 $ + */ +public abstract class SshToolsApplicationPanel extends JPanel { + // + + /** */ + protected Log log = LogFactory.getLog(SshToolsApplicationPanel.class); + + /** */ + protected SshToolsApplication application; + + /** */ + protected JMenuBar menuBar; + + /** */ + protected JToolBar toolBar; + + /** */ + protected JPopupMenu contextMenu; + + /** */ + protected SshToolsApplicationContainer container; + + /** */ + protected Vector actions = new Vector(); + + /** */ + protected HashMap actionsVisible = new HashMap(); + + /** */ + protected boolean toolsVisible; + + /** */ + protected Vector actionMenus = new Vector(); + + /** +* Creates a new SshToolsApplicationPanel object. +*/ + public SshToolsApplicationPanel() { + super(); + } + + /** +* Creates a new SshToolsApplicationPanel object. +* +* @param mgr +*/ + public SshToolsApplicationPanel(LayoutManager mgr) { + super(mgr); + } + + /** +* Called by the application framework to test the closing state +* +* @return +*/ + public abstract boolean canClose(); + + /** +* Called by the application framework to close the panel +*/ + public abstract void close(); + + /** +* Called by the application framework when a change in connection state +* has occured. The available actions should be enabled/disabled in this +* methods implementation +*/ + public abstract void setAvailableActions(); + + /** +* Set an actions visible state +* +* @param name +* @param visible +*/ + public void setActionVisible(String name, boolean visible) { + log.debug("Setting action '" + name + "' to visibility " + visible); + actionsVisible.put(name, new Boolean(visible)); + } + + /** +* Gets the container for this panel. +* +* @return +*/ + public SshToolsApplicationContainer getContainer() { + return container; + } + + /** +* Sets the container for this panel +* +* @param container +*/ + public void setContainer(SshToolsApplicationContainer container) { + this.container = container; + } + + /** +* Register a new menu +* +* @param actionMenu +*/ + public void registerActionMenu(ActionMenu actionMenu) { + ActionMenu current = getActionMenu(actionMenu.name); + + if (current == null) { + actionMenus.addElement(actionMenu); + } + } + + /** +* Gets a menu by name +* +* @param actionMenuName +* +* @return +*/ + public ActionMenu getActionMenu(String actionMenuName) { + return getActionMenu(actionMenus.iterator(), actionMenuName); + } + + private ActionMenu getActionMenu(Iterator actions, String actionMenuName) { + while (actions.hasNext()) { + ActionMenu a = (ActionMenu) actions.next(); + + if (a.name.equals(actionMenuName)) { + return a; + } + } + + return null; + } + + /** +* Get an action by name +* +* @param name +* +* @return +*/ + public StandardAction getAction(String name) { + for (Iterator i = actions.iterator(); i.hasNext();) { + StandardAction a = (StandardAction) i.next(); + + if (a.getName().equals(name)) { + return a; + } + } + + return null; + } + + /** +* Deregister an action +* +* @param action +*/ + public void deregisterAction(StandardAction action) { + actions.removeElement(action); + } + + /** +* Register a new action +* +* @param action +*/ + public void registerAction(StandardAction action) { + actions.addElement(action); + } + + /** +* Initialize the panel +* +* @param application +* +* @throws SshToolsApplicationException +*/ + public void init(SshToolsApplication application) + throws SshToolsApplicationException { + this.application = application; + menuBar = new JMenuBar(); + + // Creat the tool bar + toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setBorderPainted(false); + toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE); + + // Create the context menu + contextMenu = new JPopupMenu(); + registerActionMenu(new ActionMenu("Tools", "Tools", 't', 30)); + + if (PreferencesStore.isStoreAvailable()) { + log.debug("Preferences store is available, adding options action"); + registerAction(new OptionsAction() { + public void actionPerformed(ActionEvent evt) { + showOptions(); + } + }); + } + } + + /** +* Show the options dialog +*/ + public void showOptions() { + OptionsTab[] tabs = getApplication().getAdditionalOptionsTabs(); + OptionsPanel.showOptionsDialog(this, tabs); + } + + /** +* Rebuild all the action components such as toobar, context menu +*/ + public void rebuildActionComponents() { + // Clear the current state of the component + log.debug("Rebuild action components"); + toolBar.removeAll(); + + // + Vector enabledActions = new Vector(); + + for (Iterator i = actions.iterator(); i.hasNext();) { + StandardAction a = (StandardAction) i.next(); + String n = (String) a.getValue(Action.NAME); + Boolean s = (Boolean) actionsVisible.get(n); + + if (s == null) { + s = Boolean.TRUE; + } + + if (Boolean.TRUE.equals(s)) { + log.debug("Action " + n + " is enabled."); + enabledActions.add(a); + } else { + log.debug("Action " + n + " not enabled."); + } + } + + // Build the tool bar, grouping the actions + Vector v = new Vector(); + + for (Iterator i = enabledActions.iterator(); i.hasNext();) { + StandardAction a = (StandardAction) i.next(); + + if (Boolean.TRUE.equals( + (Boolean) a.getValue(StandardAction.ON_TOOLBAR))) { + v.addElement(a); + } + } + + Collections.sort(v, new ToolBarActionComparator()); + + Integer grp = null; + + for (Iterator i = v.iterator(); i.hasNext();) { + StandardAction z = (StandardAction) i.next(); + + if ((grp != null) && + !grp.equals( + (Integer) z.getValue(StandardAction.TOOLBAR_GROUP))) { + toolBar.add(new ToolBarSeparator()); + } + + if (Boolean.TRUE.equals( + (Boolean) z.getValue(StandardAction.IS_TOGGLE_BUTTON))) { + ToolToggleButton tBtn = new ToolToggleButton(z); + toolBar.add(tBtn); + } else { + ToolButton btn = new ToolButton(z); + toolBar.add(btn); + } + + grp = (Integer) z.getValue(StandardAction.TOOLBAR_GROUP); + } + + toolBar.revalidate(); + toolBar.repaint(); + + // Build the context menu, grouping the actions + Vector c = new Vector(); + contextMenu.removeAll(); + + for (Iterator i = enabledActions.iterator(); i.hasNext();) { + StandardAction a = (StandardAction) i.next(); + + if (Boolean.TRUE.equals( + (Boolean) a.getValue(StandardAction.ON_CONTEXT_MENU))) { + c.addElement(a); + } + } + + Collections.sort(c, new ContextActionComparator()); + grp = null; + + for (Iterator i = c.iterator(); i.hasNext();) { + StandardAction z = (StandardAction) i.next(); + + if ((grp != null) && + !grp.equals( + (Integer) z.getValue(StandardAction.CONTEXT_MENU_GROUP))) { + contextMenu.addSeparator(); + } + + contextMenu.add(z); + grp = (Integer) z.getValue(StandardAction.CONTEXT_MENU_GROUP); + } + + contextMenu.revalidate(); + + // Build the menu bar + menuBar.removeAll(); + v.removeAllElements(); + + for (Enumeration e = enabledActions.elements(); e.hasMoreElements();) { + StandardAction a = (StandardAction) e.nextElement(); + + if (Boolean.TRUE.equals( + (Boolean) a.getValue(StandardAction.ON_MENUBAR))) { + v.addElement(a); + } + } + + Vector menus = (Vector) actionMenus.clone(); + Collections.sort(menus); + + HashMap map = new HashMap(); + + for (Iterator i = v.iterator(); i.hasNext();) { + StandardAction z = (StandardAction) i.next(); + String menuName = (String) z.getValue(StandardAction.MENU_NAME); + + if (menuName == null) { + log.error("Action " + z.getName() + + " doesnt specify a value for " + StandardAction.MENU_NAME); + } else { + String m = (String) z.getValue(StandardAction.MENU_NAME); + ActionMenu menu = getActionMenu(menus.iterator(), m); + + if (menu == null) { + log.error("Action menu " + z.getName() + " does not exist"); + } else { + Vector x = (Vector) map.get(menu.name); + + if (x == null) { + x = new Vector(); + map.put(menu.name, x); + } + + x.addElement(z); + } + } + } + + for (Iterator i = menus.iterator(); i.hasNext();) { + ActionMenu m = (ActionMenu) i.next(); + Vector x = (Vector) map.get(m.name); + + if (x != null) { + Collections.sort(x, new MenuItemActionComparator()); + + JMenu menu = new JMenu(m.displayName); + menu.setMnemonic(m.mnemonic); + grp = null; + + for (Iterator j = x.iterator(); j.hasNext();) { + StandardAction a = (StandardAction) j.next(); + Integer g = (Integer) a.getValue(StandardAction.MENU_ITEM_GROUP); + + if ((grp != null) && !g.equals(grp)) { + menu.addSeparator(); + } + + grp = g; + + if (a instanceof MenuAction) { + JMenu mnu = (JMenu) a.getValue(MenuAction.MENU); + menu.add(mnu); + } else { + JMenuItem item = new JMenuItem(a); + menu.add(item); + } + } + + menuBar.add(menu); + } else { + log.error("Can't find menu " + m.name); + } + } + + menuBar.validate(); + menuBar.repaint(); + } + + /** +* Determine if the toolbar, menu and statusbar are visible +* +* @return +*/ + public boolean isToolsVisible() { + return toolsVisible; + } + + // Adds the new favorite to the appropriate favorite menu + public void addFavorite(StandardAction action) { + for (int i = 0; i < menuBar.getMenuCount(); i++) { + JMenu menu = menuBar.getMenu(i); + + if ((menu.getText() != null) && menu.getText().equals("Favorites")) { + menu.add(action); + } + } + } + + /** +* Set the visible state of the menu bar +* +* @param visible +*/ + public void setMenuBarVisible(boolean visible) { + if ((getJMenuBar() != null) && (getJMenuBar().isVisible() != visible)) { + getJMenuBar().setVisible(visible); + revalidate(); + } + } + + /** +* Set the visible state of the toolbar +* +* @param visible +*/ + public void setToolBarVisible(boolean visible) { + if ((getToolBar() != null) && (getToolBar().isVisible() != visible)) { + getToolBar().setVisible(visible); + revalidate(); + } + } + + /** +* Set the visible state of the statusbar +* +* @param visible +*/ + public void setStatusBarVisible(boolean visible) { + if ((getStatusBar() != null) && + (getStatusBar().isVisible() != visible)) { + getStatusBar().setVisible(visible); + revalidate(); + } + } + + /** +* Set the visible state of all tools. This will set the toolbar, menu and +* status bar visible states to the value provided. +* +* @param visible +*/ + public void setToolsVisible(boolean visible) { + synchronized (getTreeLock()) { + if ((getToolBar() != null) && + (getToolBar().isVisible() != visible)) { + getToolBar().setVisible(visible); + } + + if ((getJMenuBar() != null) && + (getJMenuBar().isVisible() != visible)) { + getJMenuBar().setVisible(visible); + } + + if ((getStatusBar() != null) && + (getStatusBar().isVisible() != visible)) { + getStatusBar().setVisible(visible); + } + + toolsVisible = visible; + revalidate(); + } + } + + /** +* Show an exception message +* +* @param title +* @param message +*/ + public void showExceptionMessage(String title, String message) { + JOptionPane.showMessageDialog(this, message, title, + JOptionPane.ERROR_MESSAGE); + } + + /** +* Show an error message with detail +* +* @param parent +* @param title +* @param exception +*/ + public static void showErrorMessage(Component parent, String title, + Throwable exception) { + showErrorMessage(parent, null, title, exception); + } + + /** +* Show an error message with toggable detail +* +* @param parent +* @param mesg +* @param title +* @param exception +*/ + public static void showErrorMessage(Component parent, String mesg, + String title, Throwable exception) { + boolean details = false; + + while (true) { + String[] opts = new String[] { + details ? "Hide Details" : "Details", "Ok" + }; + StringBuffer buf = new StringBuffer(); + + if (mesg != null) { + buf.append(mesg); + } + + appendException(exception, 0, buf, details); + + MultilineLabel message = new MultilineLabel(buf.toString()); + int opt = JOptionPane.showOptionDialog(parent, message, title, + JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, + null, opts, opts[1]); + + if (opt == 0) { + details = !details; + } else { + break; + } + } + } + + private static void appendException(Throwable exception, int level, + StringBuffer buf, boolean details) { + try { + if (((exception != null) && (exception.getMessage() != null)) && + (exception.getMessage().length() > 0)) { + if (details && (level > 0)) { + buf.append("\n \nCaused by ...\n"); + } + + buf.append(exception.getMessage()); + } + + if (details) { + if (exception != null) { + if ((exception.getMessage() != null) && + (exception.getMessage().length() == 0)) { + buf.append("\n \nCaused by ..."); + } else { + buf.append("\n \n"); + } + } + + StringWriter sw = new StringWriter(); + + if (exception != null) { + exception.printStackTrace(new PrintWriter(sw)); + } + + buf.append(sw.toString()); + } + + try { + java.lang.reflect.Method method = exception.getClass() + .getMethod("getCause", + new Class[] { }); + Throwable cause = (Throwable) method.invoke(exception, null); + + if (cause != null) { + appendException(cause, level + 1, buf, details); + } + } catch (Exception e) { + } + } catch (Throwable ex) { + } + } + + /** +* Returns the connected state of the panel +* +* @return +*/ + public abstract boolean isConnected(); + + /** +* Set the title of the container +* +* @param file +*/ + public void setContainerTitle(File file) { + String verString = ""; + + if (application != null) { + verString = ConfigurationLoader.getVersionString(application.getApplicationName(), + application.getApplicationVersion()); + } + + if (container != null) { + container.setContainerTitle((file == null) ? verString + : (verString + " [" + + file.getName() + "]")); + } + } + + /** +* Gets the toolbar +* +* @return +*/ + public JToolBar getToolBar() { + return toolBar; + } + + /** +* Get the context menu +* +* @return +*/ + public JPopupMenu getContextMenu() { + return contextMenu; + } + + /** +* Get the main menu +* +* @return +*/ + public JMenuBar getJMenuBar() { + return menuBar; + } + + /** +* Get the status bar +* +* @return +*/ + public StatusBar getStatusBar() { + return null; + } + + /** +* Get the application attached to the panel +* +* @return +*/ + public SshToolsApplication getApplication() { + return application; + } + + /** +* Get the icon for the panel +* +* @return +*/ + public abstract ResourceIcon getIcon(); + + public static class ActionMenu implements Comparable { + int weight; + int mnemonic; + String name; + String displayName; + + public ActionMenu(String name, String displayName, int mnemonic, + int weight) { + this.name = name; + this.displayName = displayName; + this.mnemonic = mnemonic; + this.weight = weight; + } + + public int compareTo(Object o) { + int i = new Integer(weight).compareTo(new Integer( + ((ActionMenu) o).weight)); + + return (i == 0) + ? displayName.compareTo(((ActionMenu) o).displayName) : i; + } + } + + class ToolBarActionComparator implements Comparator { + public int compare(Object o1, Object o2) { + int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.TOOLBAR_GROUP)); + + return (i == 0) + ? ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.TOOLBAR_WEIGHT)) : i; + } + } + + class ContextActionComparator implements Comparator { + public int compare(Object o1, Object o2) { + int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.CONTEXT_MENU_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.CONTEXT_MENU_GROUP)); + + return (i == 0) + ? ((Integer) ((StandardAction) o1).getValue(StandardAction.CONTEXT_MENU_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.CONTEXT_MENU_WEIGHT)) : i; + } + } + + class MenuItemActionComparator implements Comparator { + public int compare(Object o1, Object o2) { + int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.MENU_ITEM_GROUP)); + + return (i == 0) + ? ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.MENU_ITEM_WEIGHT)) : i; + } + } + + class ConnectionFileFilter extends javax.swing.filechooser.FileFilter { + public boolean accept(File f) { + return f.isDirectory() || + f.getName().toLowerCase().endsWith(".xml"); + } + + public String getDescription() { + return "Connection files (*.xml)"; + } + } +} diff --git a/src/com/sshtools/common/ui/SshToolsApplicationSessionPanel.java b/src/com/sshtools/common/ui/SshToolsApplicationSessionPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..2b670bb1b8ec45cdbffcb0e23b12fb8714e4cf7b --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsApplicationSessionPanel.java @@ -0,0 +1,268 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.connection.ChannelEventListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.LayoutManager; + +import java.io.File; +import java.io.IOException; + +import java.util.Comparator; + +import javax.swing.SwingConstants; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public abstract class SshToolsApplicationSessionPanel + extends SshToolsApplicationPanel { + /** */ + public final static String PREF_CONNECTION_FILE_DIRECTORY = "sshapps.connectionFile.directory"; + + /** */ + protected Log log = LogFactory.getLog(SshToolsApplicationSessionPanel.class); + + /** */ + protected SshToolsConnectionProfile currentConnectionProfile; + + /** */ + protected SessionManager manager; + + /** +* Creates a new SshToolsApplicationClientPanel object. +*/ + public SshToolsApplicationSessionPanel() { + super(); + } + + /** +* Creates a new SshToolsApplicationClientPanel object. +* +* @param mgr +*/ + public SshToolsApplicationSessionPanel(LayoutManager mgr) { + super(mgr); + } + + /** +* +* +* @return +*/ + public abstract SshToolsConnectionTab[] getAdditionalConnectionTabs(); + + public abstract void addEventListener(ChannelEventListener eventListener); + + public abstract boolean requiresConfiguration(); + + public abstract String getId(); + + /** +* +* +* @param manager +* @param profile +* +* @throws IOException +*/ + public final boolean openSession(SessionManager manager, + SshToolsConnectionProfile profile) throws IOException { + this.manager = manager; + + // Set the current connection properties + setCurrentConnectionProfile(profile); + + if (requiresConfiguration() && + !profile.getApplicationPropertyBoolean(getId() + ".configured", + false)) { + if (!editSettings(profile)) { + return false; + } + } + + return onOpenSession(); + } + + /** +* +* +* @throws IOException +*/ + public abstract boolean onOpenSession() throws IOException; + + /** +* +* +* @return +*/ + public boolean isConnected() { + return (manager != null) && manager.isConnected(); + } + + /** +* +* +* @param file +*/ + public void setContainerTitle(File file) { + String verString = ""; + + if (application != null) { + verString = ConfigurationLoader.getVersionString(application.getApplicationName(), + application.getApplicationVersion()); + } + + if (container != null) { + container.setContainerTitle((file == null) ? verString + : (verString + " [" + + file.getName() + "]")); + } + } + + /** +* +* +* @param profile +*/ + public void setCurrentConnectionProfile(SshToolsConnectionProfile profile) { + currentConnectionProfile = profile; + } + + /** +* +* +* @return +*/ + public SshToolsConnectionProfile getCurrentConnectionProfile() { + return currentConnectionProfile; + } + + /** +* +* +* @param profile +* +* @return +*/ + public boolean editSettings(SshToolsConnectionProfile profile) { + final SshToolsConnectionPanel panel = new SshToolsConnectionPanel(false); + SshToolsConnectionTab[] tabs = getAdditionalConnectionTabs(); + + for (int i = 0; (tabs != null) && (i < tabs.length); i++) { + tabs[i].setConnectionProfile(profile); + panel.addTab(tabs[i]); + } + + panel.setConnectionProfile(profile); + + final Option ok = new Option("Ok", + "Apply the settings and close this dialog", 'o'); + final Option cancel = new Option("Cancel", + "Close this dialog without applying the settings", 'c'); + OptionCallback callback = new OptionCallback() { + public boolean canClose(OptionsDialog dialog, Option option) { + if (option == ok) { + return panel.validateTabs(); + } + + return true; + } + }; + + OptionsDialog od = OptionsDialog.createOptionDialog(SshToolsApplicationSessionPanel.this, + new Option[] { ok, cancel }, panel, "Connection Settings", ok, + callback, null); + od.pack(); + UIUtil.positionComponent(SwingConstants.CENTER, od); + od.setVisible(true); + + if (od.getSelectedOption() == ok) { + // Apply the changes to the profile + panel.applyTabs(); + + // Ask the session manager to apply them to persistence + manager.applyProfileChanges(profile); + + return true; + } + + return false; + } + + /*public static class ActionMenu +implements Comparable { +int weight; +int mnemonic; +String name; +String displayName; +public ActionMenu(String name, String displayName, int mnemonic, + int weight) { +this.name = name; +this.displayName = displayName; +this.mnemonic = mnemonic; +this.weight = weight; +} +public int compareTo(Object o) { +int i = new Integer(weight).compareTo(new Integer( + ( (ActionMenu) o).weight)); +return (i == 0) + ? displayName.compareTo( ( (ActionMenu) o).displayName) : i; +} +}*/ + class ToolBarActionComparator implements Comparator { + public int compare(Object o1, Object o2) { + int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.TOOLBAR_GROUP)); + + return (i == 0) + ? ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.TOOLBAR_WEIGHT)) : i; + } + } + + class MenuItemActionComparator implements Comparator { + public int compare(Object o1, Object o2) { + int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.MENU_ITEM_GROUP)); + + return (i == 0) + ? ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue( + StandardAction.MENU_ITEM_WEIGHT)) : i; + } + } +} diff --git a/src/com/sshtools/common/ui/SshToolsConnectionHostTab.java b/src/com/sshtools/common/ui/SshToolsConnectionHostTab.java new file mode 100644 index 0000000000000000000000000000000000000000..8ebd2a2eb0e2af83f326da832cce986f9d46c03a --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsConnectionHostTab.java @@ -0,0 +1,419 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClientFactory; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListModel; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class SshToolsConnectionHostTab extends JPanel + implements SshToolsConnectionTab { + // + + /** */ + public final static int DEFAULT_PORT = 22; + + // + + /** */ + public final static String CONNECT_ICON = "largeserveridentity.png"; + + /** */ + public final static String AUTH_ICON = "largelock.png"; + + /** */ + public final static String SHOW_AVAILABLE = "<Show available methods>"; + + // + + /** */ + protected XTextField jTextHostname = new XTextField(); + + /** */ + protected NumericTextField jTextPort = new NumericTextField(new Integer(0), + new Integer(65535), new Integer(DEFAULT_PORT)); + + /** */ + protected XTextField jTextUsername = new XTextField(); + + /** */ + protected JList jListAuths = new JList(); + + /** */ + protected java.util.List methods = new ArrayList(); + + /** */ + protected SshToolsConnectionProfile profile; + + /** */ + protected JCheckBox allowAgentForwarding; + + /** */ + protected Log log = LogFactory.getLog(SshToolsConnectionHostTab.class); + + /** +* Creates a new SshToolsConnectionHostTab object. +*/ + public SshToolsConnectionHostTab() { + super(); + + // Create the main connection details panel + JPanel mainConnectionDetailsPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(0, 2, 2, 2); + gbc.weightx = 1.0; + + // Host name + UIUtil.jGridBagAdd(mainConnectionDetailsPanel, new JLabel("Hostname"), + gbc, GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + UIUtil.jGridBagAdd(mainConnectionDetailsPanel, jTextHostname, gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.NONE; + + // Port + UIUtil.jGridBagAdd(mainConnectionDetailsPanel, new JLabel("Port"), gbc, + GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(mainConnectionDetailsPanel, jTextPort, gbc, + GridBagConstraints.REMAINDER); + + // Username + UIUtil.jGridBagAdd(mainConnectionDetailsPanel, new JLabel("Username"), + gbc, GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weighty = 1.0; + UIUtil.jGridBagAdd(mainConnectionDetailsPanel, jTextUsername, gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.NONE; + + // + IconWrapperPanel iconMainConnectionDetailsPanel = new IconWrapperPanel(new ResourceIcon( + SshToolsConnectionHostTab.class, CONNECT_ICON), + mainConnectionDetailsPanel); + + // Authentication methods panel + JPanel authMethodsPanel = new JPanel(new GridBagLayout()); + authMethodsPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.NONE; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(2, 2, 2, 2); + gbc.weightx = 1.0; + + // Authentication methods + UIUtil.jGridBagAdd(authMethodsPanel, + new JLabel("Authentication Methods"), gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weighty = 1.0; + jListAuths.setVisibleRowCount(5); + UIUtil.jGridBagAdd(authMethodsPanel, new JScrollPane(jListAuths), gbc, + GridBagConstraints.REMAINDER); + allowAgentForwarding = new JCheckBox("Allow agent forwarding"); + UIUtil.jGridBagAdd(authMethodsPanel, allowAgentForwarding, gbc, + GridBagConstraints.REMAINDER); + + // + IconWrapperPanel iconAuthMethodsPanel = new IconWrapperPanel(new ResourceIcon( + SshToolsConnectionHostTab.class, AUTH_ICON), + authMethodsPanel); + + // This panel + setLayout(new GridBagLayout()); + setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.BOTH; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(2, 2, 2, 2); + gbc.weightx = 1.0; + UIUtil.jGridBagAdd(this, iconMainConnectionDetailsPanel, gbc, + GridBagConstraints.REMAINDER); + gbc.weighty = 1.0; + UIUtil.jGridBagAdd(this, iconAuthMethodsPanel, gbc, + GridBagConstraints.REMAINDER); + + // Set up the values in the various components + addAuthenticationMethods(); + } + + /** +* +* +* @param profile +*/ + public void setConnectionProfile(SshToolsConnectionProfile profile) { + this.profile = profile; + jTextHostname.setText((profile == null) ? "" : profile.getHost()); + jTextUsername.setText((profile == null) ? "" : profile.getUsername()); + jTextPort.setValue(new Integer((profile == null) ? 22 : profile.getPort())); + + if (System.getProperty("sshtools.agent") == null) { + allowAgentForwarding.setSelected(false); + allowAgentForwarding.setEnabled(false); + } else { + allowAgentForwarding.setEnabled(true); + allowAgentForwarding.setSelected((profile != null) && + profile.getAllowAgentForwarding()); + } + + // Match the authentication methods + Map auths = (profile == null) ? new HashMap() + : profile.getAuthenticationMethods(); + Iterator it = auths.entrySet().iterator(); + Map.Entry entry; + String authmethod; + int[] selectionarray = new int[auths.values().size()]; + int count = 0; + ListModel model = jListAuths.getModel(); + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + authmethod = (String) entry.getKey(); + + for (int i = 0; i < model.getSize(); i++) { + if (model.getElementAt(i).equals(authmethod)) { + selectionarray[count++] = i; + + break; + } + } + + /*if (jListAuths.getNextMatch(authmethod, 0, Position.Bias.Forward) > -1) { +selectionarray[count] = jListAuths.getNextMatch(authmethod, 0, +Position.Bias.Forward); +count++; +}*/ + jListAuths.clearSelection(); + jListAuths.setSelectedIndices(selectionarray); + } + } + + /** +* +* +* @return +*/ + public SshToolsConnectionProfile getConnectionProfile() { + return profile; + } + + private void addAuthenticationMethods() { + java.util.List methods = new ArrayList(); + methods.add(SHOW_AVAILABLE); + methods.addAll(SshAuthenticationClientFactory.getSupportedMethods()); + jListAuths.setListData(methods.toArray()); + jListAuths.setSelectedIndex(0); + } + + /** +* +* +* @return +*/ + public String getTabContext() { + return "Connection"; + } + + /** +* +* +* @return +*/ + public Icon getTabIcon() { + return null; + } + + /** +* +* +* @return +*/ + public String getTabTitle() { + return "Host"; + } + + /** +* +* +* @return +*/ + public String getTabToolTipText() { + return "The main host connection details."; + } + + /** +* +* +* @return +*/ + public int getTabMnemonic() { + return 'h'; + } + + /** +* +* +* @return +*/ + public Component getTabComponent() { + return this; + } + + /** +* +* +* @return +*/ + public boolean validateTab() { + // Validate that we have enough information + if (jTextHostname.getText().equals("") || + jTextPort.getText().equals("") || + jTextUsername.getText().equals("")) { + JOptionPane.showMessageDialog(this, "Please enter all details!", + "Connect", JOptionPane.OK_OPTION); + + return false; + } + + // Setup the authentications selected + java.util.List chosen = getChosenAuth(); + + if (chosen != null) { + Iterator it = chosen.iterator(); + + while (it.hasNext()) { + String method = (String) it.next(); + + try { + SshAuthenticationClient auth = SshAuthenticationClientFactory.newInstance(method); + } catch (AlgorithmNotSupportedException anse) { + JOptionPane.showMessageDialog(this, + method + " is not supported!"); + + return false; + } + } + } + + return true; + } + + private java.util.List getChosenAuth() { + // Determine whether any authenticaiton methods we selected + Object[] auths = jListAuths.getSelectedValues(); + String a; + java.util.List l = new java.util.ArrayList(); + + if (auths != null) { + for (int i = 0; i < auths.length; i++) { + a = (String) auths[i]; + + if (a.equals(SHOW_AVAILABLE)) { + return null; + } else { + l.add(a); + } + } + } else { + return null; + } + + return l; + } + + /** +* +*/ + public void applyTab() { + profile.setHost(jTextHostname.getText()); + profile.setPort(Integer.valueOf(jTextPort.getText()).intValue()); + profile.setUsername(jTextUsername.getText()); + profile.setAllowAgentForwarding(allowAgentForwarding.getModel() + .isSelected()); + + java.util.List chosen = getChosenAuth(); + + // Remove the authentication methods and re-apply them + profile.removeAuthenticationMethods(); + + if (chosen != null) { + Iterator it = chosen.iterator(); + + while (it.hasNext()) { + String method = (String) it.next(); + + try { + SshAuthenticationClient auth = SshAuthenticationClientFactory.newInstance(method); + auth.setUsername(jTextUsername.getText()); + profile.addAuthenticationMethod(auth); + } catch (AlgorithmNotSupportedException anse) { + log.error("This should have been caught by validateTab()?", + anse); + } + } + } + } + + /** +* +*/ + public void tabSelected() { + } +} diff --git a/src/com/sshtools/common/ui/SshToolsConnectionPanel.java b/src/com/sshtools/common/ui/SshToolsConnectionPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..4277030d4bf335cf2767de3581f038dbdf7fe9cd --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsConnectionPanel.java @@ -0,0 +1,264 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class SshToolsConnectionPanel extends JPanel { + // Strings + final static String DEFAULT = "<Default>"; + final static int DEFAULT_PORT = 22; + + // + + /** */ + protected Log log = LogFactory.getLog(SshToolsConnectionPanel.class); + + // + private Tabber tabber; + private SshToolsConnectionProfile profile; + + /** +* Creates a new SshToolsConnectionPanel object. +* +* @param showConnectionTabs +*/ + public SshToolsConnectionPanel(boolean showConnectionTabs) { + super(); + tabber = new Tabber(); + + if (showConnectionTabs) { + // Add the common tabs + addTab(new SshToolsConnectionHostTab()); + addTab(new SshToolsConnectionProtocolTab()); + addTab(new SshToolsConnectionProxyTab()); + } + + // Build this panel + setLayout(new GridLayout(1, 1)); + add(tabber); + } + + /** +* +* +* @return +*/ + public boolean validateTabs() { + return tabber.validateTabs(); + } + + /** +* +*/ + public void applyTabs() { + tabber.applyTabs(); + } + + /** +* +* +* @param tab +*/ + public void addTab(SshToolsConnectionTab tab) { + tabber.addTab(tab); + } + + /** +* +* +* @param profile +*/ + public void setConnectionProfile(SshToolsConnectionProfile profile) { + this.profile = profile; + + for (int i = 0; i < tabber.getTabCount(); i++) { + ((SshToolsConnectionTab) tabber.getTabAt(i)).setConnectionProfile(profile); + } + } + + /** +* +* +* @param parent +* @param optionalTabs +* +* @return +*/ + public static SshToolsConnectionProfile showConnectionDialog( + Component parent, SshToolsConnectionTab[] optionalTabs) { + return showConnectionDialog(parent, null, optionalTabs); + } + + /** +* +* +* @param parent +* @param profile +* @param optionalTabs +* +* @return +*/ + public static SshToolsConnectionProfile showConnectionDialog( + Component parent, SshToolsConnectionProfile profile, + SshToolsConnectionTab[] optionalTabs) { + // If no properties are provided, then use the default + if (profile == null) { + profile = new SshToolsConnectionProfile(); + profile.setHost(PreferencesStore.get( + SshToolsApplication.PREF_CONNECTION_LAST_HOST, "")); + profile.setPort(PreferencesStore.getInt( + SshToolsApplication.PREF_CONNECTION_LAST_PORT, DEFAULT_PORT)); + profile.setUsername(PreferencesStore.get( + SshToolsApplication.PREF_CONNECTION_LAST_USER, "")); + } + + final SshToolsConnectionPanel conx = new SshToolsConnectionPanel(true); + + if (optionalTabs != null) { + for (int i = 0; i < optionalTabs.length; i++) { + conx.addTab(optionalTabs[i]); + } + } + + conx.setConnectionProfile(profile); + + JDialog d = null; + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + + if (w instanceof JDialog) { + d = new JDialog((JDialog) w, "Connection Profile", true); + } else if (w instanceof JFrame) { + d = new JDialog((JFrame) w, "Connection Profile", true); + } else { + d = new JDialog((JFrame) null, "Connection Profile", true); + } + + final JDialog dialog = d; + + class UserAction { + boolean connect; + } + + final UserAction userAction = new UserAction(); + + // Create the bottom button panel + final JButton cancel = new JButton("Cancel"); + cancel.setMnemonic('c'); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + dialog.setVisible(false); + } + }); + + final JButton connect = new JButton("Connect"); + connect.setMnemonic('t'); + connect.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (conx.validateTabs()) { + userAction.connect = true; + dialog.setVisible(false); + } + } + }); + dialog.getRootPane().setDefaultButton(connect); + + JPanel buttonPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets = new Insets(6, 6, 0, 0); + gbc.weighty = 1.0; + UIUtil.jGridBagAdd(buttonPanel, connect, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(buttonPanel, cancel, gbc, + GridBagConstraints.REMAINDER); + + JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + southPanel.add(buttonPanel); + + // + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + mainPanel.add(conx, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + + // Show the dialog + dialog.getContentPane().setLayout(new GridLayout(1, 1)); + dialog.getContentPane().add(mainPanel); + dialog.pack(); + dialog.setResizable(false); + UIUtil.positionComponent(SwingConstants.CENTER, dialog); + dialog.setVisible(true); + + if (!userAction.connect) { + return null; + } + + conx.applyTabs(); + + // Make sure we didn't cancel + PreferencesStore.put(SshToolsApplication.PREF_CONNECTION_LAST_HOST, + profile.getHost()); + PreferencesStore.put(SshToolsApplication.PREF_CONNECTION_LAST_USER, + profile.getUsername()); + PreferencesStore.putInt(SshToolsApplication.PREF_CONNECTION_LAST_PORT, + profile.getPort()); + + // Return the connection properties + return profile; + } +} diff --git a/src/com/sshtools/common/ui/SshToolsConnectionProtocolTab.java b/src/com/sshtools/common/ui/SshToolsConnectionProtocolTab.java new file mode 100644 index 0000000000000000000000000000000000000000..efa86901b3853ea0a8838d82fc88da1ee51c0b1f --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsConnectionProtocolTab.java @@ -0,0 +1,354 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + +import com.sshtools.j2ssh.transport.cipher.SshCipherFactory; +import com.sshtools.j2ssh.transport.compression.SshCompressionFactory; +import com.sshtools.j2ssh.transport.hmac.SshHmacFactory; +import com.sshtools.j2ssh.transport.kex.SshKeyExchangeFactory; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import java.util.Iterator; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshToolsConnectionProtocolTab extends JPanel + implements SshToolsConnectionTab { + final static String KEYS_ICON = "/com/sshtools/common/ui/largekeys.png"; + final static String PROTOCOL_ICON = "/com/sshtools/common/ui/largeprotocol.png"; + final static String DEFAULT = "<Default>"; + + // + + /** */ + protected JComboBox jComboCipherCS = new JComboBox(); + + /** */ + protected JComboBox jComboCipherSC = new JComboBox(); + + /** */ + protected JComboBox jComboMacCS = new JComboBox(); + + /** */ + protected JComboBox jComboMacSC = new JComboBox(); + + /** */ + protected JComboBox jComboCompCS = new JComboBox(); + + /** */ + protected JComboBox jComboCompSC = new JComboBox(); + + /** */ + protected JComboBox jComboKex = new JComboBox(); + + /** */ + protected JComboBox jComboPK = new JComboBox(); + + /** */ + protected SshToolsConnectionProfile profile; + + /** +* Creates a new SshToolsConnectionProtocolTab object. +*/ + public SshToolsConnectionProtocolTab() { + super(); + + // Keys + JPanel keysPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.NONE; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(2, 2, 2, 2); + gbc.weightx = 1.0; + + // Public key + UIUtil.jGridBagAdd(keysPanel, new JLabel("Public key"), gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + UIUtil.jGridBagAdd(keysPanel, jComboPK, gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.NONE; + + // Public key + UIUtil.jGridBagAdd(keysPanel, new JLabel("Key exchange"), gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + UIUtil.jGridBagAdd(keysPanel, jComboKex, gbc, + GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.NONE; + + // + IconWrapperPanel iconKeysPanel = new IconWrapperPanel(new ResourceIcon( + KEYS_ICON), keysPanel); + + // Preferences + JPanel prefPanel = new JPanel(new GridBagLayout()); + prefPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.NONE; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(2, 2, 2, 2); + gbc.weightx = 1.0; + + // Public key + gbc.fill = GridBagConstraints.HORIZONTAL; + UIUtil.jGridBagAdd(prefPanel, new JLabel("Client - > Server"), gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(prefPanel, new JLabel("Server - > Client"), gbc, + GridBagConstraints.REMAINDER); + + // Separator + gbc.weightx = 2.0; + UIUtil.jGridBagAdd(prefPanel, new JSeparator(JSeparator.HORIZONTAL), + gbc, GridBagConstraints.REMAINDER); + + // Cipher + UIUtil.jGridBagAdd(prefPanel, new JLabel("Cipher"), gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(prefPanel, new JLabel("Cipher"), gbc, + GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(prefPanel, jComboCipherCS, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(prefPanel, jComboCipherSC, gbc, + GridBagConstraints.REMAINDER); + + // Mac + UIUtil.jGridBagAdd(prefPanel, new JLabel("Mac"), gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(prefPanel, new JLabel("Mac"), gbc, + GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(prefPanel, jComboMacCS, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(prefPanel, jComboMacSC, gbc, + GridBagConstraints.REMAINDER); + + // Compression + UIUtil.jGridBagAdd(prefPanel, new JLabel("Compression"), gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(prefPanel, new JLabel("Compression"), gbc, + GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(prefPanel, jComboCompCS, gbc, + GridBagConstraints.RELATIVE); + UIUtil.jGridBagAdd(prefPanel, jComboCompSC, gbc, + GridBagConstraints.REMAINDER); + + // + IconWrapperPanel iconPrefPanel = new IconWrapperPanel(new ResourceIcon( + PROTOCOL_ICON), prefPanel); + + // This tab + setLayout(new GridBagLayout()); + setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.NONE; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(2, 2, 2, 2); + gbc.weightx = 1.0; + UIUtil.jGridBagAdd(this, iconKeysPanel, gbc, + GridBagConstraints.REMAINDER); + gbc.weighty = 1.0; + UIUtil.jGridBagAdd(this, iconPrefPanel, gbc, + GridBagConstraints.REMAINDER); + + // + loadList(SshCipherFactory.getSupportedCiphers(), jComboCipherCS, true); + loadList(SshCipherFactory.getSupportedCiphers(), jComboCipherSC, true); + loadList(SshHmacFactory.getSupportedMacs(), jComboMacCS, true); + loadList(SshHmacFactory.getSupportedMacs(), jComboMacSC, true); + loadList(SshCompressionFactory.getSupportedCompression(), jComboCompCS, + true); + loadList(SshCompressionFactory.getSupportedCompression(), jComboCompSC, + true); + loadList(SshKeyExchangeFactory.getSupportedKeyExchanges(), jComboKex, + true); + loadList(SshKeyPairFactory.getSupportedKeys(), jComboPK, true); + } + + /** +* +* +* @param profile +*/ + public void setConnectionProfile(SshToolsConnectionProfile profile) { + this.profile = profile; + jComboCipherCS.setSelectedItem(profile.getPrefCSEncryption()); + jComboCipherSC.setSelectedItem(profile.getPrefSCEncryption()); + jComboMacCS.setSelectedItem(profile.getPrefCSMac()); + jComboMacSC.setSelectedItem(profile.getPrefSCMac()); + jComboCompCS.setSelectedItem(profile.getPrefCSComp()); + jComboCompSC.setSelectedItem(profile.getPrefSCComp()); + jComboKex.setSelectedItem(profile.getPrefKex()); + jComboPK.setSelectedItem(profile.getPrefPublicKey()); + } + + /** +* +* +* @return +*/ + public SshToolsConnectionProfile getConnectionProfile() { + return profile; + } + + private void loadList(java.util.List list, JComboBox combo, + boolean addDefault) { + Iterator it = list.iterator(); + + if (addDefault) { + combo.addItem(DEFAULT); + } + + while (it.hasNext()) { + combo.addItem(it.next()); + } + } + + /** +* +* +* @return +*/ + public String getTabContext() { + return "Connection"; + } + + /** +* +* +* @return +*/ + public Icon getTabIcon() { + return null; + } + + /** +* +* +* @return +*/ + public String getTabTitle() { + return "Protocol"; + } + + /** +* +* +* @return +*/ + public String getTabToolTipText() { + return "Protocol related properties."; + } + + /** +* +* +* @return +*/ + public int getTabMnemonic() { + return 'p'; + } + + /** +* +* +* @return +*/ + public Component getTabComponent() { + return this; + } + + /** +* +* +* @return +*/ + public boolean validateTab() { + return true; + } + + /** +* +*/ + public void applyTab() { + // Get the algorithm preferences + if (!jComboCipherCS.getSelectedItem().equals(DEFAULT)) { + profile.setPrefCSEncryption((String) jComboCipherCS.getSelectedItem()); + } + + if (!jComboCipherSC.getSelectedItem().equals(DEFAULT)) { + profile.setPrefSCEncryption((String) jComboCipherSC.getSelectedItem()); + } + + if (!jComboMacCS.getSelectedItem().equals(DEFAULT)) { + profile.setPrefCSMac((String) jComboMacCS.getSelectedItem()); + } + + if (!jComboMacSC.getSelectedItem().equals(DEFAULT)) { + profile.setPrefSCMac((String) jComboMacSC.getSelectedItem()); + } + + if (!jComboCompCS.getSelectedItem().equals(DEFAULT)) { + profile.setPrefCSComp((String) jComboCompCS.getSelectedItem()); + } + + if (!jComboCompSC.getSelectedItem().equals(DEFAULT)) { + profile.setPrefSCComp((String) jComboCompSC.getSelectedItem()); + } + + if (!jComboKex.getSelectedItem().equals(DEFAULT)) { + profile.setPrefKex((String) jComboKex.getSelectedItem()); + } + + if (!jComboPK.getSelectedItem().equals(DEFAULT)) { + profile.setPrefPublicKey((String) jComboPK.getSelectedItem()); + } + } + + /** +* +*/ + public void tabSelected() { + } +} diff --git a/src/com/sshtools/common/ui/SshToolsConnectionProxyTab.java b/src/com/sshtools/common/ui/SshToolsConnectionProxyTab.java new file mode 100644 index 0000000000000000000000000000000000000000..96ad39504e52935b33050845f4606c55754136dc --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsConnectionProxyTab.java @@ -0,0 +1,333 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshToolsConnectionProxyTab extends JPanel + implements SshToolsConnectionTab { + /** */ + public final static String PROXY_ICON = "/com/sshtools/common/ui/proxy.png"; + + /** */ + protected JRadioButton noProxy = new JRadioButton("No proxy"); + + /** */ + protected JRadioButton httpProxy = new JRadioButton("HTTP"); + + /** */ + protected JRadioButton socks4Proxy = new JRadioButton("SOCKS 4"); + + /** */ + protected JRadioButton socks5Proxy = new JRadioButton("SOCKS 5"); + + /** */ + protected ButtonGroup group = new ButtonGroup(); + + /** */ + protected JPanel proxyframe = new JPanel(new GridBagLayout()); + + /** */ + protected JTextField username = new JTextField(); + + /** */ + protected JPasswordField password = new JPasswordField(); + + /** */ + protected JTextField proxy = new JTextField(); + + /** */ + protected NumericTextField port = new NumericTextField(new Integer(1), + new Integer(65535)); + + /** */ + protected SshToolsConnectionProfile profile; + + /** */ + protected Log log = LogFactory.getLog(SshToolsConnectionProxyTab.class); + + /** +* Creates a new SshToolsConnectionProxyTab object. +*/ + public SshToolsConnectionProxyTab() { + super(); + group.add(noProxy); + group.add(httpProxy); + group.add(socks4Proxy); + group.add(socks5Proxy); + + ChangeListener listener = new ChangeListener() { + public void stateChanged(ChangeEvent e) { + if (noProxy.isSelected()) { + username.setEnabled(false); + password.setEnabled(false); + proxy.setEnabled(false); + + //port.setEnabled(false); + port.setForeground(Color.white); + } else { + username.setEnabled(true); + password.setEnabled(true); + proxy.setEnabled(true); + + //port.setEnabled(true); + port.setForeground(Color.black); + + if (httpProxy.isSelected()) { + port.setText("80"); + } else { + port.setText("1080"); + } + } + } + }; + + noProxy.getModel().addChangeListener(listener); + httpProxy.getModel().addChangeListener(listener); + socks4Proxy.getModel().addChangeListener(listener); + socks5Proxy.getModel().addChangeListener(listener); + + // Create the main connection details panel + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTH; + gbc.insets = new Insets(0, 2, 2, 2); + gbc.weightx = 1.0; + proxyframe.setBorder(BorderFactory.createTitledBorder( + "Connect using the following proxy")); + + // No proxy label + gbc.insets = new Insets(2, 10, 2, 2); + UIUtil.jGridBagAdd(proxyframe, noProxy, gbc, GridBagConstraints.RELATIVE); + + // Socks 4 label + gbc.insets = new Insets(2, 15, 2, 2); + UIUtil.jGridBagAdd(proxyframe, socks4Proxy, gbc, + GridBagConstraints.REMAINDER); + + //gbc.fill = GridBagConstraints.HORIZONTAL; + // Http Proxy + gbc.insets = new Insets(2, 10, 2, 2); + UIUtil.jGridBagAdd(proxyframe, httpProxy, gbc, + GridBagConstraints.RELATIVE); + + // Socks 5 label + gbc.insets = new Insets(2, 15, 2, 2); + UIUtil.jGridBagAdd(proxyframe, socks5Proxy, gbc, + GridBagConstraints.REMAINDER); + gbc.insets = new Insets(2, 10, 2, 10); + + JPanel connect = new JPanel(new GridBagLayout()); + connect.setBorder(BorderFactory.createTitledBorder("Proxy Details")); + UIUtil.jGridBagAdd(connect, new JLabel("Host"), gbc, + GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(connect, proxy, gbc, GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(connect, new JLabel("Port"), gbc, + GridBagConstraints.REMAINDER); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.NONE; + UIUtil.jGridBagAdd(connect, port, gbc, GridBagConstraints.REMAINDER); + gbc.fill = GridBagConstraints.HORIZONTAL; + UIUtil.jGridBagAdd(connect, new JLabel("Username"), gbc, + GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(connect, username, gbc, GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(connect, new JLabel("Password"), gbc, + GridBagConstraints.REMAINDER); + gbc.insets = new Insets(2, 10, 10, 10); + UIUtil.jGridBagAdd(connect, password, gbc, GridBagConstraints.REMAINDER); + + JPanel main = new JPanel(new GridBagLayout()); + gbc.insets = new Insets(2, 2, 2, 2); + UIUtil.jGridBagAdd(main, proxyframe, gbc, GridBagConstraints.REMAINDER); + UIUtil.jGridBagAdd(main, connect, gbc, GridBagConstraints.REMAINDER); + + IconWrapperPanel iconProxyDetailsPanel = new IconWrapperPanel(new ResourceIcon( + PROXY_ICON), main); + noProxy.setSelected(true); + + // This panel + setLayout(new BorderLayout()); + setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTH; + gbc.insets = new Insets(2, 2, 2, 2); + gbc.weightx = 1.0; + add(iconProxyDetailsPanel, BorderLayout.NORTH); + } + + /** +* +* +* @param profile +*/ + public void setConnectionProfile(SshToolsConnectionProfile profile) { + this.profile = profile; + + if (profile.getTransportProvider() == SshToolsConnectionProfile.USE_HTTP_PROXY) { + httpProxy.setSelected(true); + } else if (profile.getTransportProvider() == SshToolsConnectionProfile.USE_SOCKS4_PROXY) { + socks4Proxy.setSelected(true); + } else if (profile.getTransportProvider() == SshToolsConnectionProfile.USE_SOCKS5_PROXY) { + socks5Proxy.setSelected(true); + } + + proxy.setText(profile.getProxyHost()); + + if (profile.getProxyPort() > 0) { + port.setValue(new Integer(profile.getProxyPort())); + } + + username.setText(profile.getProxyUsername()); + password.setText(profile.getProxyPassword()); + } + + /** +* +* +* @return +*/ + public SshToolsConnectionProfile getConnectionProfile() { + return profile; + } + + /** +* +* +* @return +*/ + public String getTabContext() { + return "Connection"; + } + + /** +* +* +* @return +*/ + public Icon getTabIcon() { + return null; + } + + /** +* +* +* @return +*/ + public String getTabTitle() { + return "Proxy"; + } + + /** +* +* +* @return +*/ + public String getTabToolTipText() { + return "Configure the proxy connection."; + } + + /** +* +* +* @return +*/ + public int getTabMnemonic() { + return 'p'; + } + + /** +* +* +* @return +*/ + public Component getTabComponent() { + return this; + } + + /** +* +* +* @return +*/ + public boolean validateTab() { + return true; + } + + /** +* +*/ + public void applyTab() { + if (httpProxy.isSelected()) { + profile.setTransportProvider(SshToolsConnectionProfile.USE_HTTP_PROXY); + } else if (socks4Proxy.isSelected()) { + profile.setTransportProvider(SshToolsConnectionProfile.USE_SOCKS4_PROXY); + } else if (socks5Proxy.isSelected()) { + profile.setTransportProvider(SshToolsConnectionProfile.USE_SOCKS5_PROXY); + } else { + profile.setTransportProvider(SshToolsConnectionProfile.USE_STANDARD_SOCKET); + } + + profile.setProxyHost(proxy.getText()); + profile.setProxyPort(port.getValue().intValue()); + profile.setProxyUsername(username.getText()); + profile.setProxyPassword(new String(password.getPassword())); + } + + /** +* +*/ + public void tabSelected() { + } +} diff --git a/src/com/sshtools/common/ui/SshToolsConnectionTab.java b/src/com/sshtools/common/ui/SshToolsConnectionTab.java new file mode 100644 index 0000000000000000000000000000000000000000..f5c64926e52665417edddb6b4f69d3b387863c56 --- /dev/null +++ b/src/com/sshtools/common/ui/SshToolsConnectionTab.java @@ -0,0 +1,51 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.common.configuration.SshToolsConnectionProfile; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface SshToolsConnectionTab extends Tab { + /** +* +* +* @param profile +*/ + public void setConnectionProfile(SshToolsConnectionProfile profile); + + /** +* +* +* @return +*/ + public SshToolsConnectionProfile getConnectionProfile(); +} diff --git a/src/com/sshtools/common/ui/StandardAction.java b/src/com/sshtools/common/ui/StandardAction.java new file mode 100644 index 0000000000000000000000000000000000000000..3df31dd0f29cc1cd3d670ade797c74c762f8ff84 --- /dev/null +++ b/src/com/sshtools/common/ui/StandardAction.java @@ -0,0 +1,198 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.*; + +import java.net.*; + +import javax.swing.*; +import javax.swing.event.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public abstract class StandardAction extends AbstractAction { + /** */ + public final static String ON_TOOLBAR = "onToolBar"; + + /** */ + public final static String TOOLBAR_GROUP = "toolBarGroup"; + + /** */ + public final static String TOOLBAR_WEIGHT = "toolBarWeight"; + + /** */ + public final static String ON_MENUBAR = "onMenuBar"; + + /** */ + public final static String MENU_NAME = "menuName"; + + /** */ + public final static String MENU_ITEM_GROUP = "menuItemGroup"; + + /** */ + public final static String MENU_ITEM_WEIGHT = "menuItemWeight"; + + /** */ + public final static String IMAGE_DIR = "/com/sshtools/sshterm/"; + + /** */ + public final static String HIDE_TOOLBAR_TEXT = "hideToolbarText"; + + /** */ + public final static String IS_TOGGLE_BUTTON = "isToggleButton"; + + /** */ + public final static String LARGE_ICON = "LargeIcon"; + + /** */ + public final static String ON_CONTEXT_MENU = "onContextMenu"; + + /** */ + public final static String CONTEXT_MENU_GROUP = "contextMenuGroup"; + + /** */ + public final static String CONTEXT_MENU_WEIGHT = "contextMenuWeight"; + + /** */ + public final static String MENU_ICON = "menuIcon"; + + // The listener to action events (usually the main UI) + private EventListenerList listeners; + + /** +* +* +* @return +*/ + public String getActionCommand() { + return (String) getValue(Action.ACTION_COMMAND_KEY); + } + + /** +* +* +* @return +*/ + public String getShortDescription() { + return (String) getValue(Action.SHORT_DESCRIPTION); + } + + /** +* +* +* @return +*/ + public String getLongDescription() { + return (String) getValue(Action.LONG_DESCRIPTION); + } + + /** +* +* +* @return +*/ + public String getName() { + return (String) getValue(Action.NAME); + } + + /** +* +* +* @return +*/ + public String getSmallIcon() { + return (String) getValue(Action.SMALL_ICON); + } + + /** +* +* +* @param evt +*/ + public void actionPerformed(ActionEvent evt) { + if (listeners != null) { + Object[] listenerList = listeners.getListenerList(); + + // Recreate the ActionEvent and stuff the value of the ACTION_COMMAND_KEY + ActionEvent e = new ActionEvent(evt.getSource(), evt.getID(), + (String) getValue(Action.ACTION_COMMAND_KEY)); + + for (int i = 0; i <= (listenerList.length - 2); i += 2) { + ((ActionListener) listenerList[i + 1]).actionPerformed(e); + } + } + } + + /** +* +* +* @param l +*/ + public void addActionListener(ActionListener l) { + if (listeners == null) { + listeners = new EventListenerList(); + } + + listeners.add(ActionListener.class, l); + } + + /** +* +* +* @param l +*/ + public void removeActionListener(ActionListener l) { + if (listeners == null) { + return; + } + + listeners.remove(ActionListener.class, l); + } + + /** +* +* +* @param name +* +* @return +*/ + public ImageIcon getIcon(String name) { + String imagePath = name.startsWith("/") ? name : (IMAGE_DIR + name); + URL url = this.getClass().getResource(imagePath); + + if (url != null) { + return new ImageIcon(url); + } + + return null; + } +} diff --git a/src/com/sshtools/common/ui/StartStopStateRenderer.java b/src/com/sshtools/common/ui/StartStopStateRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..731224b9ae0f588dbd13dd3a96d03ae4eb3932b2 --- /dev/null +++ b/src/com/sshtools/common/ui/StartStopStateRenderer.java @@ -0,0 +1,88 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import com.sshtools.j2ssh.util.StartStopState; + +import java.awt.Component; + +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + + +public class StartStopStateRenderer extends DefaultTableCellRenderer { + // Private instance variables + private Icon startedIcon; + + // Private instance variables + private Icon stoppedIcon; + + // Private instance variables + private Icon failedIcon; + + // Private instance variables + private String errorMsg; + + public StartStopStateRenderer(Icon startedIcon, Icon stoppedIcon) { + this.startedIcon = startedIcon; + this.stoppedIcon = stoppedIcon; + setHorizontalAlignment(JLabel.CENTER); + } + + public StartStopStateRenderer(Icon startedIcon, Icon stoppedIcon, + Icon failedIcon, String errorMsg) { + this.startedIcon = startedIcon; + this.stoppedIcon = stoppedIcon; + this.failedIcon = failedIcon; + this.errorMsg = errorMsg; + setHorizontalAlignment(JLabel.CENTER); + } + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + + StartStopState state = (StartStopState) value; + + if (state.getValue() == StartStopState.FAILED) { + setIcon(failedIcon); + setToolTipText(errorMsg); + } else { + setIcon((state.getValue() == StartStopState.STOPPED) ? stoppedIcon + : startedIcon); + setToolTipText(null); + } + + return this; + } + + public String getText() { + return null; + } +} diff --git a/src/com/sshtools/common/ui/StatusBar.java b/src/com/sshtools/common/ui/StatusBar.java new file mode 100644 index 0000000000000000000000000000000000000000..1e632c47bb9aa12664984d76da730a6a31e84a66 --- /dev/null +++ b/src/com/sshtools/common/ui/StatusBar.java @@ -0,0 +1,211 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JPanel; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class StatusBar extends JPanel { + /** */ + public final static Icon GREEN_LED_ON = new ResourceIcon(StatusBar.class, + "greenledon.png"); + + /** */ + public final static Icon GREEN_LED_OFF = new ResourceIcon(StatusBar.class, + "greenledoff.png"); + + /** */ + public final static Icon RED_LED_ON = new ResourceIcon(StatusBar.class, + "redledon.png"); + + /** */ + public final static Icon RED_LED_OFF = new ResourceIcon(StatusBar.class, + "redledoff.png"); + + // + private StatusLabel connected; + + // + private StatusLabel statusText; + + // + private StatusLabel host; + + // + private StatusLabel user; + + // + private StatusLabel rid; + private JLabel sending; + private JLabel receiving; + private javax.swing.Timer sendingTimer; + private javax.swing.Timer receivingTimer; + + /** +* Creates a new StatusBar object. +*/ + public StatusBar() { + super(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.BOTH; + gbc.weightx = 0.0; + connected = new StatusLabel(RED_LED_OFF); + connected.setHorizontalAlignment(JLabel.CENTER); + UIUtil.jGridBagAdd(this, connected, gbc, 1); + + JPanel lights = new JPanel(new GridLayout(1, 2)); + sending = new JLabel(GREEN_LED_OFF); + sending.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); + sending.setHorizontalAlignment(JLabel.CENTER); + receiving = new JLabel(GREEN_LED_OFF); + receiving.setHorizontalAlignment(JLabel.CENTER); + lights.add(sending); + lights.add(receiving); + gbc.weightx = 0.0; + lights.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLoweredBevelBorder(), + BorderFactory.createEmptyBorder(1, 1, 1, 1))); + gbc.weightx = 1.5; + host = new StatusLabel(); + UIUtil.jGridBagAdd(this, host, gbc, 1); + user = new StatusLabel(); + UIUtil.jGridBagAdd(this, user, gbc, 1); + rid = new StatusLabel(); + UIUtil.jGridBagAdd(this, rid, gbc, 1); + statusText = new StatusLabel(); + gbc.weightx = 4.0; + UIUtil.jGridBagAdd(this, statusText, gbc, GridBagConstraints.RELATIVE); + gbc.weightx = 0.0; + UIUtil.jGridBagAdd(this, lights, gbc, GridBagConstraints.REMAINDER); + } + + /** +* +* +* @param receiving +*/ + public void setReceiving(boolean receiving) { + this.receiving.setIcon(receiving ? GREEN_LED_ON : GREEN_LED_OFF); + } + + /** +* +* +* @param sending +*/ + public void setSending(boolean sending) { + this.sending.setIcon(sending ? GREEN_LED_ON : GREEN_LED_OFF); + } + + /** +* +* +* @param connected +*/ + public void setConnected(boolean connected) { + this.connected.setIcon(connected ? RED_LED_ON : RED_LED_OFF); + } + + /** +* +* +* @param text +*/ + public void setStatusText(String text) { + statusText.setText(text); + } + + /** +* +* +* @param text +*/ + public void setHost(String text) { + host.setText(text); + } + + /** +* +* +* @param text +* @param port +*/ + public void setHost(String text, int port) { + host.setText(text + ":" + String.valueOf(port)); + } + + /** +* +* +* @param remoteId +*/ + public void setRemoteId(String remoteId) { + rid.setText(remoteId); + } + + /** +* +* +* @param text +*/ + public void setUser(String text) { + user.setText(text); + } + + class StatusLabel extends JLabel { + StatusLabel(Icon icon) { + super(icon); + init(); + } + + StatusLabel() { + super(); + init(); + } + + void init() { + setFont(getFont().deriveFont(10f)); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLoweredBevelBorder(), + BorderFactory.createEmptyBorder(1, 1, 1, 1))); + } + } +} diff --git a/src/com/sshtools/common/ui/StopAction.java b/src/com/sshtools/common/ui/StopAction.java new file mode 100644 index 0000000000000000000000000000000000000000..fe93bdd5a30408ecec501997a009e9d9231352df --- /dev/null +++ b/src/com/sshtools/common/ui/StopAction.java @@ -0,0 +1,69 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.event.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class StopAction extends StandardAction { + private final static String ACTION_COMMAND_KEY_STOP = "stop-command"; + private final static String NAME_STOP = "Stop"; + private final static String SMALL_ICON_STOP = "/com/sshtools/common/ui/stop.png"; + private final static String LARGE_ICON_STOP = ""; + private final static String SHORT_DESCRIPTION_STOP = "Stop recording"; + private final static String LONG_DESCRIPTION_STOP = "Stop recording output to file"; + private final static int MNEMONIC_KEY_STOP = 's'; + + /** +* Creates a new StopAction object. +*/ + public StopAction() { + putValue(Action.NAME, NAME_STOP); + putValue(Action.SMALL_ICON, getIcon(SMALL_ICON_STOP)); + putValue(LARGE_ICON, getIcon(LARGE_ICON_STOP)); + putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION_STOP); + putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION_STOP); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.ALT_MASK)); + putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC_KEY_STOP)); + putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND_KEY_STOP); + putValue(StandardAction.ON_MENUBAR, new Boolean(true)); + putValue(StandardAction.MENU_NAME, "File"); + putValue(StandardAction.MENU_ITEM_GROUP, new Integer(60)); + putValue(StandardAction.MENU_ITEM_WEIGHT, new Integer(10)); + putValue(StandardAction.ON_TOOLBAR, new Boolean(true)); + putValue(StandardAction.TOOLBAR_GROUP, new Integer(60)); + putValue(StandardAction.TOOLBAR_WEIGHT, new Integer(10)); + } +} diff --git a/src/com/sshtools/common/ui/Tab.java b/src/com/sshtools/common/ui/Tab.java new file mode 100644 index 0000000000000000000000000000000000000000..383df6515aaf4512712886b035a05b907dcf08fe --- /dev/null +++ b/src/com/sshtools/common/ui/Tab.java @@ -0,0 +1,98 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface Tab { + /** +* +* +* @return +*/ + public String getTabContext(); + + /** +* +* +* @return +*/ + public Icon getTabIcon(); + + /** +* +* +* @return +*/ + public String getTabTitle(); + + /** +* +* +* @return +*/ + public String getTabToolTipText(); + + /** +* +* +* @return +*/ + public int getTabMnemonic(); + + /** +* +* +* @return +*/ + public Component getTabComponent(); + + /** +* +* +* @return +*/ + public boolean validateTab(); + + /** +* +*/ + public void applyTab(); + + /** +* +*/ + public void tabSelected(); +} diff --git a/src/com/sshtools/common/ui/Tabber.java b/src/com/sshtools/common/ui/Tabber.java new file mode 100644 index 0000000000000000000000000000000000000000..9ff7b99a3953cf3315c631d8e310de9cedf3d8e0 --- /dev/null +++ b/src/com/sshtools/common/ui/Tabber.java @@ -0,0 +1,128 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.event.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class Tabber extends JTabbedPane { + /** +* Creates a new Tabber object. +*/ + public Tabber() { + this(TOP); + } + + /** +* Creates a new Tabber object. +* +* @param tabPlacement +*/ + public Tabber(int tabPlacement) { + super(tabPlacement); + addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + if (getSelectedIndex() != -1) { + getTabAt(getSelectedIndex()).tabSelected(); + } + } + }); + } + + /** +* +* +* @param i +* +* @return +*/ + public Tab getTabAt(int i) { + return ((TabPanel) getComponentAt(i)).getTab(); + } + + /** +* +* +* @return +*/ + public boolean validateTabs() { + for (int i = 0; i < getTabCount(); i++) { + Tab tab = ((TabPanel) getComponentAt(i)).getTab(); + + if (!tab.validateTab()) { + setSelectedIndex(i); + + return false; + } + } + + return true; + } + + /** +* +*/ + public void applyTabs() { + for (int i = 0; i < getTabCount(); i++) { + Tab tab = ((TabPanel) getComponentAt(i)).getTab(); + tab.applyTab(); + } + } + + /** +* +* +* @param tab +*/ + public void addTab(Tab tab) { + addTab(tab.getTabTitle(), tab.getTabIcon(), new TabPanel(tab), + tab.getTabToolTipText()); + } + + class TabPanel extends JPanel { + private Tab tab; + + TabPanel(Tab tab) { + super(new BorderLayout()); + this.tab = tab; + setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + add(tab.getTabComponent(), BorderLayout.CENTER); + } + + public Tab getTab() { + return tab; + } + } +} diff --git a/src/com/sshtools/common/ui/TextBox.java b/src/com/sshtools/common/ui/TextBox.java new file mode 100644 index 0000000000000000000000000000000000000000..1751085e58f8618927cc69d3591f6579d7c40e6a --- /dev/null +++ b/src/com/sshtools/common/ui/TextBox.java @@ -0,0 +1,60 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import javax.swing.*; + + +/** + * Description of the Class + * + *@author magicthize + *@created 26 May 2002 + */ +public class TextBox extends JTextArea { + private String text; + + public TextBox() { + this(""); + } + + public TextBox(String text) { + this(text, 0, 0); + } + + public TextBox(String text, int rows, int columns) { + super(rows, columns); + setBackground(UIManager.getColor("Label.background")); + setForeground(UIManager.getColor("Label.foreground")); + setBorder(UIManager.getBorder("Label.border")); + setFont(UIManager.getFont("TextField.font")); + setOpaque(false); + setWrapStyleWord(true); + setLineWrap(true); + setEditable(false); + setText(text); + } +} diff --git a/src/com/sshtools/common/ui/ToolBarSeparator.java b/src/com/sshtools/common/ui/ToolBarSeparator.java new file mode 100644 index 0000000000000000000000000000000000000000..dd54f81c257ab5976782007b54eb8166f9340827 --- /dev/null +++ b/src/com/sshtools/common/ui/ToolBarSeparator.java @@ -0,0 +1,65 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ToolBarSeparator extends JSeparator { + /** +* Creates a new ToolBarSeparator object. +*/ + public ToolBarSeparator() { + super(JSeparator.VERTICAL); + } + + /** +* +* +* @return +*/ + public Dimension getMaximumSize() { + return (((JToolBar) getParent()).getOrientation() == JToolBar.HORIZONTAL) + ? new Dimension(4, super.getMaximumSize().height) + : new Dimension(super.getMaximumSize().width, 4); + } + + /** +* +*/ + public void doLayout() { + setOrientation((((JToolBar) getParent()).getOrientation() == JToolBar.HORIZONTAL) + ? JSeparator.VERTICAL : JSeparator.HORIZONTAL); + } +} diff --git a/src/com/sshtools/common/ui/ToolButton.java b/src/com/sshtools/common/ui/ToolButton.java new file mode 100644 index 0000000000000000000000000000000000000000..20de156cd323f494a29d8edd1d15e24eee0a3d2c --- /dev/null +++ b/src/com/sshtools/common/ui/ToolButton.java @@ -0,0 +1,95 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class ToolButton extends JButton { + // + private final static Insets INSETS = new Insets(0, 0, 0, 0); + private boolean hideText; + + /** +* Creates a new ToolButton object. +* +* @param action +*/ + public ToolButton(Action action) { + super(action); + setMargin(INSETS); + setRequestFocusEnabled(false); + setFocusPainted(false); + + if (action.getValue(StandardAction.HIDE_TOOLBAR_TEXT) != null) { + setHideText(Boolean.TRUE.equals( + (Boolean) action.getValue(StandardAction.HIDE_TOOLBAR_TEXT))); + } else { + setHideText(true); + } + } + + /** +* +* +* @return +*/ + public boolean isFocusable() { + return false; + } + + /** +* +* +* @param hideText +*/ + public void setHideText(boolean hideText) { + if (this.hideText != hideText) { + firePropertyChange("hideText", this.hideText, hideText); + } + + this.hideText = hideText; + this.setHorizontalTextPosition(ToolButton.RIGHT); + repaint(); + } + + /** +* +* +* @return +*/ + public String getText() { + return hideText ? null : super.getText(); + } +} diff --git a/src/com/sshtools/common/ui/ToolToggleButton.java b/src/com/sshtools/common/ui/ToolToggleButton.java new file mode 100644 index 0000000000000000000000000000000000000000..e5299c4b5d29f4172ca27de8d921aa7165762078 --- /dev/null +++ b/src/com/sshtools/common/ui/ToolToggleButton.java @@ -0,0 +1,98 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.2 $ + */ +public class ToolToggleButton extends JToggleButton { + // + private final static Insets INSETS = new Insets(0, 0, 0, 0); + private boolean hideText; + + /** +* Creates a new ToolButton object. +* +* @param action +*/ + public ToolToggleButton(Action action) { + super(action); + setMargin(INSETS); + setRequestFocusEnabled(false); + setFocusPainted(false); + + if (action.getValue(StandardAction.HIDE_TOOLBAR_TEXT) != null) { + setHideText(Boolean.TRUE.equals( + (Boolean) action.getValue(StandardAction.HIDE_TOOLBAR_TEXT))); + } else { + setHideText(true); + } + } + + /** +* +* +* @return +*/ + public boolean isFocusable() { + return false; + } + + /** +* +* +* @param hideText +*/ + public void setHideText(boolean hideText) { + if (this.hideText != hideText) { + firePropertyChange("hideText", this.hideText, hideText); + } + + if (hideText) { + this.hideText = hideText; + this.setHorizontalTextPosition(ToolButton.RIGHT); + } + + repaint(); + } + + /** +* +* +* @return +*/ + public String getText() { + return hideText ? null : super.getText(); + } +} diff --git a/src/com/sshtools/common/ui/UIUtil.java b/src/com/sshtools/common/ui/UIUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..2d24ae1f807589e72216cb2ec55cebde4e0d0f66 --- /dev/null +++ b/src/com/sshtools/common/ui/UIUtil.java @@ -0,0 +1,183 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; +import java.awt.event.*; + +import java.util.*; + +import javax.swing.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class UIUtil implements SwingConstants { + /** +* Parse a string in the format of <code>[character]</code> to create an +* Integer that may be used for an action. +* +* @param character mnemonic string +* @return mnemonic +*/ + public static Integer parseMnemonicString(String string) { + try { + return new Integer(string); + } catch (Throwable t) { + return new Integer(-1); + } + } + + /** +* Parse a string in the format of [ALT+|CTRL+|SHIFT+]<keycode> to +* create a keystroke. This can be used to define accelerators from +* resource bundles +* +* @param string accelerator string +* @return keystroke +*/ + public static KeyStroke parseAcceleratorString(String string) { + if ((string == null) || string.equals("")) { + return null; + } + + StringTokenizer t = new StringTokenizer(string, "+"); + int mod = 0; + int key = -1; + + while (t.hasMoreTokens()) { + String x = t.nextToken(); + + if (x.equalsIgnoreCase("ctrl")) { + mod += KeyEvent.CTRL_MASK; + } else if (x.equalsIgnoreCase("shift")) { + mod += KeyEvent.SHIFT_MASK; + } else if (x.equalsIgnoreCase("alt")) { + mod += KeyEvent.ALT_MASK; + } else { + try { + java.lang.reflect.Field f = KeyEvent.class.getField(x); + key = f.getInt(null); + } catch (Throwable ex) { + ex.printStackTrace(); + } + } + } + + if (key != -1) { + KeyStroke ks = KeyStroke.getKeyStroke(key, mod); + + return ks; + } + + return null; + } + + /** +* +* +* @param parent +* @param componentToAdd +* @param constraints +* @param pos +* +* @throws IllegalArgumentException +*/ + public static void jGridBagAdd(JComponent parent, Component componentToAdd, + GridBagConstraints constraints, int pos) { + if (!(parent.getLayout() instanceof GridBagLayout)) { + throw new IllegalArgumentException( + "parent must have a GridBagLayout"); + } + + // + GridBagLayout layout = (GridBagLayout) parent.getLayout(); + + // + constraints.gridwidth = pos; + layout.setConstraints(componentToAdd, constraints); + parent.add(componentToAdd); + } + + /** +* +* +* @param p +* @param c +*/ + public static void positionComponent(int p, Component c) { + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + + switch (p) { + case NORTH_WEST: + c.setLocation(0, 0); + + break; + + case NORTH: + c.setLocation((d.width - c.getSize().width) / 2, 0); + + break; + + case NORTH_EAST: + c.setLocation((d.width - c.getSize().width), 0); + + break; + + case WEST: + c.setLocation(0, (d.height - c.getSize().height) / 2); + + break; + + case SOUTH_WEST: + c.setLocation(0, (d.height - c.getSize().height)); + + break; + + case EAST: + c.setLocation(d.width - c.getSize().width, + (d.height - c.getSize().height) / 2); + + break; + + case SOUTH_EAST: + c.setLocation((d.width - c.getSize().width), + (d.height - c.getSize().height) - 30); + + break; + + case CENTER: + c.setLocation((d.width - c.getSize().width) / 2, + (d.height - c.getSize().height) / 2); + + break; + } + } +} diff --git a/src/com/sshtools/common/ui/XTextField.java b/src/com/sshtools/common/ui/XTextField.java new file mode 100644 index 0000000000000000000000000000000000000000..b889a0534db68b808ddef77fd095b59863b04046 --- /dev/null +++ b/src/com/sshtools/common/ui/XTextField.java @@ -0,0 +1,254 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.ui; + +import java.awt.*; +import java.awt.datatransfer.*; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.text.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class XTextField extends JTextField implements ClipboardOwner { + private JPopupMenu popup; + private Action cutAction; + private Action copyAction; + private Action pasteAction; + private Action deleteAction; + private Action selectAllAction; + + /** +* Creates a new XTextField object. +*/ + public XTextField() { + this(null, null, 0); + } + + /** +* Creates a new XTextField object. +* +* @param text +*/ + public XTextField(String text) { + this(null, text, 0); + } + + /** +* Creates a new XTextField object. +* +* @param columns +*/ + public XTextField(int columns) { + this(null, null, columns); + } + + /** +* Creates a new XTextField object. +* +* @param text +* @param columns +*/ + public XTextField(String text, int columns) { + this(null, text, columns); + } + + /** +* Creates a new XTextField object. +* +* @param doc +* @param text +* @param columns +*/ + public XTextField(Document doc, String text, int columns) { + super(doc, text, columns); + initXtensions(); + } + + /** +* +* +* @param clipboard +* @param contents +*/ + public void lostOwnership(Clipboard clipboard, Transferable contents) { + } + + private void showPopup(int x, int y) { + // Grab the focus, this should deselect any other selected fields. + requestFocus(); + + // If the popup has never been show before - then build it + if (popup == null) { + popup = new JPopupMenu("Clipboard"); + popup.add(cutAction = new CutAction()); + popup.add(copyAction = new CopyAction()); + popup.add(pasteAction = new PasteAction()); + popup.add(deleteAction = new DeleteAction()); + popup.addSeparator(); + popup.add(selectAllAction = new SelectAllAction()); + } + + // Enabled the actions based on the field contents + cutAction.setEnabled(isEnabled() && (getSelectedText() != null)); + copyAction.setEnabled(isEnabled() && (getSelectedText() != null)); + deleteAction.setEnabled(isEnabled() && (getSelectedText() != null)); + pasteAction.setEnabled(isEnabled() && + Toolkit.getDefaultToolkit().getSystemClipboard().getContents(this) + .isDataFlavorSupported(DataFlavor.stringFlavor)); + selectAllAction.setEnabled(isEnabled()); + + // Make the popup visible + popup.show(this, x, y); + } + + private void initXtensions() { + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + if (SwingUtilities.isRightMouseButton(evt)) { + showPopup(evt.getX(), evt.getY()); + } + } + }); + addFocusListener(new FocusListener() { + public void focusGained(FocusEvent evt) { + XTextField.this.selectAll(); + } + + public void focusLost(FocusEvent evt) { + // if(popup.isVisible()) + // popup.setVisible(false); + } + }); + } + + // Supporting actions + class CopyAction extends AbstractAction { + public CopyAction() { + putValue(Action.NAME, "Copy"); + putValue(Action.SMALL_ICON, + new ResourceIcon(XTextField.class, "copy.png")); + putValue(Action.SHORT_DESCRIPTION, "Copy"); + putValue(Action.LONG_DESCRIPTION, + "Copy the selection from the text and place it in the clipboard"); + putValue(Action.MNEMONIC_KEY, new Integer('c')); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK)); + } + + public void actionPerformed(ActionEvent evt) { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection( + getText()), XTextField.this); + } + } + + class CutAction extends AbstractAction { + public CutAction() { + putValue(Action.NAME, "Cut"); + putValue(Action.SMALL_ICON, + new ResourceIcon(XTextField.class, "cut.png")); + putValue(Action.SHORT_DESCRIPTION, "Cut selection"); + putValue(Action.LONG_DESCRIPTION, + "Cut the selection from the text and place it in the clipboard"); + putValue(Action.MNEMONIC_KEY, new Integer('u')); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_MASK)); + } + + public void actionPerformed(ActionEvent evt) { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection( + getText()), XTextField.this); + setText(""); + } + } + + class PasteAction extends AbstractAction { + public PasteAction() { + putValue(Action.NAME, "Paste"); + putValue(Action.SMALL_ICON, + new ResourceIcon(XTextField.class, "paste.png")); + putValue(Action.SHORT_DESCRIPTION, "Paste clipboard content"); + putValue(Action.LONG_DESCRIPTION, + "Paste the clipboard contents to the current care position or replace the selection"); + putValue(Action.MNEMONIC_KEY, new Integer('p')); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_MASK)); + } + + public void actionPerformed(ActionEvent evt) { + Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard() + .getContents(this); + + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + try { + setText(t.getTransferData(DataFlavor.stringFlavor).toString()); + } catch (Exception e) { + // Dont care + } + } + } + } + + class DeleteAction extends AbstractAction { + public DeleteAction() { + putValue(Action.NAME, "Delete"); + putValue(Action.SMALL_ICON, + new ResourceIcon(XTextField.class, "delete.png")); + putValue(Action.SHORT_DESCRIPTION, "Delete selection"); + putValue(Action.LONG_DESCRIPTION, + "Delete the selection from the text"); + putValue(Action.MNEMONIC_KEY, new Integer('d')); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_MASK)); + } + + public void actionPerformed(ActionEvent evt) { + setText(""); + } + } + + class SelectAllAction extends AbstractAction { + SelectAllAction() { + putValue(Action.SMALL_ICON, new EmptyIcon(16, 16)); + putValue(Action.NAME, "Select All"); + putValue(Action.SHORT_DESCRIPTION, "Select All"); + putValue(Action.LONG_DESCRIPTION, "Select all items in the context"); + putValue(Action.MNEMONIC_KEY, new Integer('a')); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_MASK)); + } + + public void actionPerformed(ActionEvent evt) { + selectAll(); + } + } +} diff --git a/src/com/sshtools/common/ui/about.png b/src/com/sshtools/common/ui/about.png new file mode 100644 index 0000000000000000000000000000000000000000..f971c8aca8831c5f7cf031de4e1a78e73f52d795 Binary files /dev/null and b/src/com/sshtools/common/ui/about.png differ diff --git a/src/com/sshtools/common/ui/add.png b/src/com/sshtools/common/ui/add.png new file mode 100644 index 0000000000000000000000000000000000000000..1d903dac0a94d56ea38d663ffe4742900bee6c30 Binary files /dev/null and b/src/com/sshtools/common/ui/add.png differ diff --git a/src/com/sshtools/common/ui/cancel.png b/src/com/sshtools/common/ui/cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..10a64c18e12565199d513ea2f78e37d23d41aa2e Binary files /dev/null and b/src/com/sshtools/common/ui/cancel.png differ diff --git a/src/com/sshtools/common/ui/close.png b/src/com/sshtools/common/ui/close.png new file mode 100644 index 0000000000000000000000000000000000000000..972bfa369a59676478742c1258a283da4c52ce39 Binary files /dev/null and b/src/com/sshtools/common/ui/close.png differ diff --git a/src/com/sshtools/common/ui/commands.png b/src/com/sshtools/common/ui/commands.png new file mode 100644 index 0000000000000000000000000000000000000000..af26d0649687084d682a51fdd9477fec464eb2cb Binary files /dev/null and b/src/com/sshtools/common/ui/commands.png differ diff --git a/src/com/sshtools/common/ui/copy.png b/src/com/sshtools/common/ui/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..ee3793110461b9d3fe58358a4b4cfe87252d2de4 Binary files /dev/null and b/src/com/sshtools/common/ui/copy.png differ diff --git a/src/com/sshtools/common/ui/cut.png b/src/com/sshtools/common/ui/cut.png new file mode 100644 index 0000000000000000000000000000000000000000..4ea39c4bf9757314b6effb39d85aa06ee7f4aed9 Binary files /dev/null and b/src/com/sshtools/common/ui/cut.png differ diff --git a/src/com/sshtools/common/ui/delete.png b/src/com/sshtools/common/ui/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..173da46973c680420d57120cc29a99670759a8c5 Binary files /dev/null and b/src/com/sshtools/common/ui/delete.png differ diff --git a/src/com/sshtools/common/ui/dialog-error4.png b/src/com/sshtools/common/ui/dialog-error4.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a6717216fba6bbc0ed901dea2810bd94d791ee Binary files /dev/null and b/src/com/sshtools/common/ui/dialog-error4.png differ diff --git a/src/com/sshtools/common/ui/dialog-information.png b/src/com/sshtools/common/ui/dialog-information.png new file mode 100644 index 0000000000000000000000000000000000000000..8afa062fbcb7eb8076bcee6174f28f2692246105 Binary files /dev/null and b/src/com/sshtools/common/ui/dialog-information.png differ diff --git a/src/com/sshtools/common/ui/dialog-question3.png b/src/com/sshtools/common/ui/dialog-question3.png new file mode 100644 index 0000000000000000000000000000000000000000..dfa69846f1ac23710acb0acb35b24c95f75a4dca Binary files /dev/null and b/src/com/sshtools/common/ui/dialog-question3.png differ diff --git a/src/com/sshtools/common/ui/dialog-warning2.png b/src/com/sshtools/common/ui/dialog-warning2.png new file mode 100644 index 0000000000000000000000000000000000000000..3c8a37df51861ef31171987ef06c59fffa044f61 Binary files /dev/null and b/src/com/sshtools/common/ui/dialog-warning2.png differ diff --git a/src/com/sshtools/common/ui/fileedit.png b/src/com/sshtools/common/ui/fileedit.png new file mode 100644 index 0000000000000000000000000000000000000000..61c0cc4a4725059b604755ecf39d437d84253fb9 Binary files /dev/null and b/src/com/sshtools/common/ui/fileedit.png differ diff --git a/src/com/sshtools/common/ui/fileopen.png b/src/com/sshtools/common/ui/fileopen.png new file mode 100644 index 0000000000000000000000000000000000000000..97f9988f016c4b9cd75dd758d4f58125491df9b7 Binary files /dev/null and b/src/com/sshtools/common/ui/fileopen.png differ diff --git a/src/com/sshtools/common/ui/global.png b/src/com/sshtools/common/ui/global.png new file mode 100644 index 0000000000000000000000000000000000000000..430969cc3036610948ca6f9a01f3a1ac59830377 Binary files /dev/null and b/src/com/sshtools/common/ui/global.png differ diff --git a/src/com/sshtools/common/ui/greenledoff.png b/src/com/sshtools/common/ui/greenledoff.png new file mode 100644 index 0000000000000000000000000000000000000000..86518102cbfc5cdaffb75dd00e94456800f7fe31 Binary files /dev/null and b/src/com/sshtools/common/ui/greenledoff.png differ diff --git a/src/com/sshtools/common/ui/greenledon.png b/src/com/sshtools/common/ui/greenledon.png new file mode 100644 index 0000000000000000000000000000000000000000..36569e7191160c2c52603ac0ad763dc40f5e7a28 Binary files /dev/null and b/src/com/sshtools/common/ui/greenledon.png differ diff --git a/src/com/sshtools/common/ui/im.gif b/src/com/sshtools/common/ui/im.gif new file mode 100644 index 0000000000000000000000000000000000000000..aff60374d1dd86541e833f51b6b4604c47ef1485 Binary files /dev/null and b/src/com/sshtools/common/ui/im.gif differ diff --git a/src/com/sshtools/common/ui/im.png b/src/com/sshtools/common/ui/im.png new file mode 100644 index 0000000000000000000000000000000000000000..b957e5897e8d1ef2e95d3bccc514602d13bc741a Binary files /dev/null and b/src/com/sshtools/common/ui/im.png differ diff --git a/src/com/sshtools/common/ui/largecard.png b/src/com/sshtools/common/ui/largecard.png new file mode 100644 index 0000000000000000000000000000000000000000..37f4d74500c39edb282c578e73bd67d3911d72de Binary files /dev/null and b/src/com/sshtools/common/ui/largecard.png differ diff --git a/src/com/sshtools/common/ui/largeglobal.png b/src/com/sshtools/common/ui/largeglobal.png new file mode 100644 index 0000000000000000000000000000000000000000..d1c25d06a333dbe950e5c123c4ff13201f5bb1c8 Binary files /dev/null and b/src/com/sshtools/common/ui/largeglobal.png differ diff --git a/src/com/sshtools/common/ui/largekeys.png b/src/com/sshtools/common/ui/largekeys.png new file mode 100644 index 0000000000000000000000000000000000000000..e474d703237c955cad6ccb84b743badc0a35e41f Binary files /dev/null and b/src/com/sshtools/common/ui/largekeys.png differ diff --git a/src/com/sshtools/common/ui/largelock.png b/src/com/sshtools/common/ui/largelock.png new file mode 100644 index 0000000000000000000000000000000000000000..8b1540ff064d0c5f3fcd2e385ba31ebee200f2c2 Binary files /dev/null and b/src/com/sshtools/common/ui/largelock.png differ diff --git a/src/com/sshtools/common/ui/largenewwindow.png b/src/com/sshtools/common/ui/largenewwindow.png new file mode 100644 index 0000000000000000000000000000000000000000..dbcff4eca57f2b2ebcbb36b9e181939db84f269a Binary files /dev/null and b/src/com/sshtools/common/ui/largenewwindow.png differ diff --git a/src/com/sshtools/common/ui/largeoptions.png b/src/com/sshtools/common/ui/largeoptions.png new file mode 100644 index 0000000000000000000000000000000000000000..89bc37e094683e2f33ea5ec87c59160b2875e1ec Binary files /dev/null and b/src/com/sshtools/common/ui/largeoptions.png differ diff --git a/src/com/sshtools/common/ui/largeprotocol.png b/src/com/sshtools/common/ui/largeprotocol.png new file mode 100644 index 0000000000000000000000000000000000000000..384d3f48ec5764dcd7e5e73c93704060e6aa39a7 Binary files /dev/null and b/src/com/sshtools/common/ui/largeprotocol.png differ diff --git a/src/com/sshtools/common/ui/largeserveridentity.png b/src/com/sshtools/common/ui/largeserveridentity.png new file mode 100644 index 0000000000000000000000000000000000000000..e8e474f635fcc8299e37bf121acc90b5a2146848 Binary files /dev/null and b/src/com/sshtools/common/ui/largeserveridentity.png differ diff --git a/src/com/sshtools/common/ui/newconnect.png b/src/com/sshtools/common/ui/newconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..e6e1a8ef60f8f159e3ba4161885863b4a94ce2c5 Binary files /dev/null and b/src/com/sshtools/common/ui/newconnect.png differ diff --git a/src/com/sshtools/common/ui/newwindow.png b/src/com/sshtools/common/ui/newwindow.png new file mode 100644 index 0000000000000000000000000000000000000000..7c8b5e04689f69bcbd2f8fb42832c7fd928aa5c7 Binary files /dev/null and b/src/com/sshtools/common/ui/newwindow.png differ diff --git a/src/com/sshtools/common/ui/ok.png b/src/com/sshtools/common/ui/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..5381fa2c49272fefd139b8a3076aae7be8fa77dd Binary files /dev/null and b/src/com/sshtools/common/ui/ok.png differ diff --git a/src/com/sshtools/common/ui/options.png b/src/com/sshtools/common/ui/options.png new file mode 100644 index 0000000000000000000000000000000000000000..db4b86f2e7ddf4a21bf3c7ffc68df2b091eba172 Binary files /dev/null and b/src/com/sshtools/common/ui/options.png differ diff --git a/src/com/sshtools/common/ui/padlock.png b/src/com/sshtools/common/ui/padlock.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3b000f9dabec1ed7416dc0c2641a6b5aa50adc Binary files /dev/null and b/src/com/sshtools/common/ui/padlock.png differ diff --git a/src/com/sshtools/common/ui/password.png b/src/com/sshtools/common/ui/password.png new file mode 100644 index 0000000000000000000000000000000000000000..4d531929b20bf9dc2802baa0e96968b303354539 Binary files /dev/null and b/src/com/sshtools/common/ui/password.png differ diff --git a/src/com/sshtools/common/ui/paste.png b/src/com/sshtools/common/ui/paste.png new file mode 100644 index 0000000000000000000000000000000000000000..f9e7d9df56c47421de2aeba8f4b19139333f81d8 Binary files /dev/null and b/src/com/sshtools/common/ui/paste.png differ diff --git a/src/com/sshtools/common/ui/print.png b/src/com/sshtools/common/ui/print.png new file mode 100644 index 0000000000000000000000000000000000000000..961657146a4169e66c15290b76336f098972b2b1 Binary files /dev/null and b/src/com/sshtools/common/ui/print.png differ diff --git a/src/com/sshtools/common/ui/printpreview.png b/src/com/sshtools/common/ui/printpreview.png new file mode 100644 index 0000000000000000000000000000000000000000..459883f278a8e3cf4423ef6656bd727bc2077b46 Binary files /dev/null and b/src/com/sshtools/common/ui/printpreview.png differ diff --git a/src/com/sshtools/common/ui/properties.png b/src/com/sshtools/common/ui/properties.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb9c1fb2b8eef79b5b54907ab9014b4c3953e3d Binary files /dev/null and b/src/com/sshtools/common/ui/properties.png differ diff --git a/src/com/sshtools/common/ui/proxy.png b/src/com/sshtools/common/ui/proxy.png new file mode 100644 index 0000000000000000000000000000000000000000..ee8837a7352406be540b41e0f385986f3258b6ff Binary files /dev/null and b/src/com/sshtools/common/ui/proxy.png differ diff --git a/src/com/sshtools/common/ui/record.png b/src/com/sshtools/common/ui/record.png new file mode 100644 index 0000000000000000000000000000000000000000..ec4906c502e5b79926125e64ceeb1a525ceff1e6 Binary files /dev/null and b/src/com/sshtools/common/ui/record.png differ diff --git a/src/com/sshtools/common/ui/redledoff.png b/src/com/sshtools/common/ui/redledoff.png new file mode 100644 index 0000000000000000000000000000000000000000..545c1ceea7c0823ab3ccb91719452db5b7e93b78 Binary files /dev/null and b/src/com/sshtools/common/ui/redledoff.png differ diff --git a/src/com/sshtools/common/ui/redledon.png b/src/com/sshtools/common/ui/redledon.png new file mode 100644 index 0000000000000000000000000000000000000000..453c7b4d9c170be1422840e4867148726d0d3b5e Binary files /dev/null and b/src/com/sshtools/common/ui/redledon.png differ diff --git a/src/com/sshtools/common/ui/refresh.png b/src/com/sshtools/common/ui/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..e1ce850ca4d0890b541936d3a96b7c30691e8d45 Binary files /dev/null and b/src/com/sshtools/common/ui/refresh.png differ diff --git a/src/com/sshtools/common/ui/remove.png b/src/com/sshtools/common/ui/remove.png new file mode 100644 index 0000000000000000000000000000000000000000..4492040612bd933eec37eb1e0668b5b9d2f9a49f Binary files /dev/null and b/src/com/sshtools/common/ui/remove.png differ diff --git a/src/com/sshtools/common/ui/save.png b/src/com/sshtools/common/ui/save.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc9a7a872cf391d7459b7b39e1bf9d823cacc8d Binary files /dev/null and b/src/com/sshtools/common/ui/save.png differ diff --git a/src/com/sshtools/common/ui/stop.png b/src/com/sshtools/common/ui/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..06614ee7e6d1a68e380ca126fb675fd5ba84c81b Binary files /dev/null and b/src/com/sshtools/common/ui/stop.png differ diff --git a/src/com/sshtools/common/util/BrowserLauncher.java b/src/com/sshtools/common/util/BrowserLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..82049d5b6dd25ebd4e9aff1c568e5173963eeeaa --- /dev/null +++ b/src/com/sshtools/common/util/BrowserLauncher.java @@ -0,0 +1,442 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + +import java.io.*; + +import java.lang.reflect.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class BrowserLauncher { + private static int jvm; + private static Object browser; + private static boolean loadedWithoutErrors; + private static Class mrjFileUtilsClass; + private static Class mrjOSTypeClass; + private static Class macOSErrorClass; + private static Class aeDescClass; + private static Constructor aeTargetConstructor; + private static Constructor appleEventConstructor; + private static Constructor aeDescConstructor; + private static Method findFolder; + private static Method getFileType; + private static Method makeOSType; + private static Method putParameter; + private static Method sendNoReply; + private static Object kSystemFolderType; + private static Integer keyDirectObject; + private static Integer kAutoGenerateReturnID; + private static Integer kAnyTransactionID; + private static final int MRJ_2_0 = 0; + private static final int MRJ_2_1 = 1; + private static final int WINDOWS_NT = 2; + private static final int WINDOWS_9x = 3; + private static final int OTHER = -1; + private static final String FINDER_TYPE = "FNDR"; + private static final String FINDER_CREATOR = "MACS"; + private static final String GURL_EVENT = "GURL"; + private static final String FIRST_WINDOWS_PARAMETER = "/c"; + private static final String SECOND_WINDOWS_PARAMETER = "start"; + private static final String NETSCAPE_OPEN_PARAMETER_START = " -remote 'openURL("; + private static final String NETSCAPE_OPEN_PARAMETER_END = ")'"; + private static String errorMessage; + + static { + loadedWithoutErrors = true; + + String osName = System.getProperty("os.name"); + + if ("Mac OS".equals(osName)) { + String mrjVersion = System.getProperty("mrj.version"); + String majorMRJVersion = mrjVersion.substring(0, 3); + + try { + double version = Double.valueOf(majorMRJVersion).doubleValue(); + + if (version == 2) { + jvm = MRJ_2_0; + } else if (version >= 2.1) { + // For the time being, assume that all post-2.0 versions of MRJ work the same + jvm = MRJ_2_1; + } else { + loadedWithoutErrors = false; + errorMessage = "Unsupported MRJ version: " + version; + } + } catch (NumberFormatException nfe) { + loadedWithoutErrors = false; + errorMessage = "Invalid MRJ version: " + mrjVersion; + } + } else if (osName.startsWith("Windows")) { + if (osName.indexOf("9") != -1) { + jvm = WINDOWS_9x; + } else { + jvm = WINDOWS_NT; + } + } else { + jvm = OTHER; + } + + if (loadedWithoutErrors) { // if we haven't hit any errors yet + loadedWithoutErrors = loadClasses(); + } + } + + private BrowserLauncher() { + } + + private static boolean loadClasses() { + switch (jvm) { + case MRJ_2_0: + + try { + Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget"); + macOSErrorClass = Class.forName("com.apple.MacOS.MacOSError"); + + Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils"); + Class appleEventClass = Class.forName( + "com.apple.MacOS.AppleEvent"); + Class aeClass = Class.forName("com.apple.MacOS.ae"); + aeDescClass = Class.forName("com.apple.MacOS.AEDesc"); + aeTargetConstructor = aeTargetClass.getDeclaredConstructor(new Class[] { + int.class + }); + appleEventConstructor = appleEventClass.getDeclaredConstructor(new Class[] { + int.class, int.class, aeTargetClass, int.class, + int.class + }); + aeDescConstructor = aeDescClass.getDeclaredConstructor(new Class[] { + String.class + }); + makeOSType = osUtilsClass.getDeclaredMethod("makeOSType", + new Class[] { String.class }); + putParameter = appleEventClass.getDeclaredMethod("putParameter", + new Class[] { int.class, aeDescClass }); + sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply", + new Class[] { }); + + Field keyDirectObjectField = aeClass.getDeclaredField( + "keyDirectObject"); + keyDirectObject = (Integer) keyDirectObjectField.get(null); + + Field autoGenerateReturnIDField = appleEventClass.getDeclaredField( + "kAutoGenerateReturnID"); + kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField.get(null); + + Field anyTransactionIDField = appleEventClass.getDeclaredField( + "kAnyTransactionID"); + kAnyTransactionID = (Integer) anyTransactionIDField.get(null); + } catch (ClassNotFoundException cnfe) { + errorMessage = cnfe.getMessage(); + + return false; + } catch (NoSuchMethodException nsme) { + errorMessage = nsme.getMessage(); + + return false; + } catch (NoSuchFieldException nsfe) { + errorMessage = nsfe.getMessage(); + + return false; + } catch (IllegalAccessException iae) { + errorMessage = iae.getMessage(); + + return false; + } + + break; + + case MRJ_2_1: + + try { + mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils"); + mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType"); + + Field systemFolderField = mrjFileUtilsClass.getDeclaredField( + "kSystemFolderType"); + kSystemFolderType = systemFolderField.get(null); + findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder", + new Class[] { mrjOSTypeClass }); + getFileType = mrjFileUtilsClass.getDeclaredMethod("getFileType", + new Class[] { File.class }); + } catch (ClassNotFoundException cnfe) { + errorMessage = cnfe.getMessage(); + + return false; + } catch (NoSuchFieldException nsfe) { + errorMessage = nsfe.getMessage(); + + return false; + } catch (NoSuchMethodException nsme) { + errorMessage = nsme.getMessage(); + + return false; + } catch (SecurityException se) { + errorMessage = se.getMessage(); + + return false; + } catch (IllegalAccessException iae) { + errorMessage = iae.getMessage(); + + return false; + } + + break; + } + + return true; + } + + private static Object locateBrowser() { + if (browser != null) { + return browser; + } + + switch (jvm) { + case MRJ_2_0: + + try { + Integer finderCreatorCode = (Integer) makeOSType.invoke(null, + new Object[] { FINDER_CREATOR }); + Object aeTarget = aeTargetConstructor.newInstance(new Object[] { + finderCreatorCode + }); + Integer gurlType = (Integer) makeOSType.invoke(null, + new Object[] { GURL_EVENT }); + Object appleEvent = appleEventConstructor.newInstance(new Object[] { + gurlType, gurlType, aeTarget, kAutoGenerateReturnID, + kAnyTransactionID + }); + + // Don't set browser = appleEvent because then the next time we call + // locateBrowser(), we'll get the same AppleEvent, to which we'll already have + // added the relevant parameter. Instead, regenerate the AppleEvent every time. + // There's probably a way to do this better; if any has any ideas, please let + // me know. + return appleEvent; + } catch (IllegalAccessException iae) { + browser = null; + errorMessage = iae.getMessage(); + + return browser; + } catch (InstantiationException ie) { + browser = null; + errorMessage = ie.getMessage(); + + return browser; + } catch (InvocationTargetException ite) { + browser = null; + errorMessage = ite.getMessage(); + + return browser; + } + + case MRJ_2_1: + + File systemFolder; + + try { + systemFolder = (File) findFolder.invoke(null, + new Object[] { kSystemFolderType }); + } catch (IllegalArgumentException iare) { + browser = null; + errorMessage = iare.getMessage(); + + return browser; + } catch (IllegalAccessException iae) { + browser = null; + errorMessage = iae.getMessage(); + + return browser; + } catch (InvocationTargetException ite) { + browser = null; + errorMessage = ite.getTargetException().getClass() + ": " + + ite.getTargetException().getMessage(); + + return browser; + } + + String[] systemFolderFiles = systemFolder.list(); + + // Avoid a FilenameFilter because that can't be stopped mid-list + for (int i = 0; i < systemFolderFiles.length; i++) { + try { + File file = new File(systemFolder, systemFolderFiles[i]); + + if (!file.isFile()) { + continue; + } + + Object fileType = getFileType.invoke(null, + new Object[] { file }); + + if (FINDER_TYPE.equals(fileType.toString())) { + browser = file.toString(); // Actually the Finder, but that's OK + + return browser; + } + } catch (IllegalArgumentException iare) { + browser = browser; + errorMessage = iare.getMessage(); + + return null; + } catch (IllegalAccessException iae) { + browser = null; + errorMessage = iae.getMessage(); + + return browser; + } catch (InvocationTargetException ite) { + browser = null; + errorMessage = ite.getTargetException().getClass() + ": " + + ite.getTargetException().getMessage(); + + return browser; + } + } + + browser = null; + + break; + + case WINDOWS_NT: + browser = "cmd.exe"; + + break; + + case WINDOWS_9x: + browser = "command.com"; + + break; + + case OTHER:default: + + //browser = "netscape"; surely mozilla is the thing these days + browser = "mozilla"; + + break; + } + + return browser; + } + + /** +* +* +* @param url +* +* @throws IOException +*/ + public static void openURL(String url) throws IOException { + if (!loadedWithoutErrors) { + throw new IOException("Exception in finding browser: " + + errorMessage); + } + + Object browser = locateBrowser(); + + if (browser == null) { + throw new IOException("Unable to locate browser: " + errorMessage); + } + + switch (jvm) { + case MRJ_2_0: + + Object aeDesc = null; + + try { + aeDesc = aeDescConstructor.newInstance(new Object[] { url }); + putParameter.invoke(browser, + new Object[] { keyDirectObject, aeDesc }); + sendNoReply.invoke(browser, new Object[] { }); + } catch (InvocationTargetException ite) { + throw new IOException( + "InvocationTargetException while creating AEDesc: " + + ite.getMessage()); + } catch (IllegalAccessException iae) { + throw new IOException( + "IllegalAccessException while building AppleEvent: " + + iae.getMessage()); + } catch (InstantiationException ie) { + throw new IOException( + "InstantiationException while creating AEDesc: " + + ie.getMessage()); + } finally { + aeDesc = null; // Encourage it to get disposed if it was created + browser = null; // Ditto + } + + break; + + case MRJ_2_1: + Runtime.getRuntime().exec(new String[] { (String) browser, url }); + + break; + + case WINDOWS_NT: + case WINDOWS_9x: + Runtime.getRuntime().exec(new String[] { + (String) browser, FIRST_WINDOWS_PARAMETER, + SECOND_WINDOWS_PARAMETER, url + }); + + break; + + case OTHER: + + // Assume that we're on Unix and that Netscape is installed + // First, attempt to open the URL in a currently running session of Netscape + Process process = Runtime.getRuntime().exec((String) browser + + NETSCAPE_OPEN_PARAMETER_START + url + + NETSCAPE_OPEN_PARAMETER_END); + + try { + int exitCode = process.waitFor(); + + if (exitCode != 0) { // if Netscape was not open + Runtime.getRuntime().exec(new String[] { (String) browser, url }); + } + } catch (InterruptedException ie) { + throw new IOException( + "InterruptedException while launching browser: " + + ie.getMessage()); + } + + break; + + default: + + // This should never occur, but if it does, we'll try the simplest thing possible + Runtime.getRuntime().exec(new String[] { (String) browser, url }); + + break; + } + } +} diff --git a/src/com/sshtools/common/util/GeneralUtil.java b/src/com/sshtools/common/util/GeneralUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..b7d4f3e71b9afd9386a09f3654180be5485e66f2 --- /dev/null +++ b/src/com/sshtools/common/util/GeneralUtil.java @@ -0,0 +1,148 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + +import java.awt.*; + + +/** + * Other utilities not worth their own class + */ +public final class GeneralUtil { + /** +* This method will replace '&' with "&", '"' with """, '<' with "<" and '>' with ">". +* +* @param html html to encode +* @return encoded html +*/ + public static String encodeHTML(String html) { + // Does java have a method of doing this? + StringBuffer buf = new StringBuffer(); + char ch; + + for (int i = 0; i < html.length(); i++) { + ch = html.charAt(i); + + switch (ch) { + case '&': + + // May be already encoded + if (((i + 5) < html.length()) && + html.substring(i + 1, i + 5).equals("amp;")) { + buf.append(ch); + } else { + buf.append("&"); + } + + break; + + case '"': + buf.append("""); + + break; + + case '<': + buf.append("<"); + + break; + + case '>': + buf.append(">"); + + break; + + default: + buf.append(ch); + } + } + + return buf.toString(); + } + + /** +* Return a <code>Color</code> object given a string representation of it +* +* @param color +* @return string representation +* @throws IllegalArgumentException if string in bad format +*/ + public static Color stringToColor(String s) { + try { + return new Color(Integer.decode("0x" + s.substring(1, 3)).intValue(), + Integer.decode("0x" + s.substring(3, 5)).intValue(), + Integer.decode("0x" + s.substring(5, 7)).intValue()); + } catch (Exception e) { + return null; + } + } + + /** +* Return a string representation of a color +* +* @param color +* @return string representation +*/ + public static String colorToString(Color color) { + if (color == null) { + return ""; + } + + StringBuffer buf = new StringBuffer(); + buf.append('#'); + buf.append(numberToPaddedHexString(color.getRed(), 2)); + buf.append(numberToPaddedHexString(color.getGreen(), 2)); + buf.append(numberToPaddedHexString(color.getBlue(), 2)); + + return buf.toString(); + } + + /** +* Convert a number to a zero padded hex string +* +* @param int number +* @return zero padded hex string +* @throws IllegalArgumentException if number takes up more characters than +* <code>size</code> +*/ + public static String numberToPaddedHexString(int number, int size) { + String s = Integer.toHexString(number); + + if (s.length() > size) { + throw new IllegalArgumentException( + "Number too big for padded hex string"); + } + + StringBuffer buf = new StringBuffer(); + + for (int i = 0; i < (size - s.length()); i++) { + buf.append('0'); + } + + buf.append(s); + + return buf.toString(); + } +} diff --git a/src/com/sshtools/common/util/JVMUtil.java b/src/com/sshtools/common/util/JVMUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..14cedf294dedce1e23d3fe7548408e47cdad9986 --- /dev/null +++ b/src/com/sshtools/common/util/JVMUtil.java @@ -0,0 +1,67 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class JVMUtil { + /** +* +* +* @return +*/ + public static int getMajorVersion() { + return 1; + } + + /** +* +* +* @return +*/ + public static int getMinorVersion() { + return 4; + } + + /** +* +* +* @param args +*/ + public static void main(String[] args) { + System.getProperties().list(System.out); + System.out.println("Major=" + getMajorVersion()); + System.out.println("Minor=" + getMinorVersion()); + } +} + + +// end class Base64 diff --git a/src/com/sshtools/common/util/PropertyUtil.java b/src/com/sshtools/common/util/PropertyUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..c0c4ee935738555a10edaa7e5d93cffac5b8f0cb --- /dev/null +++ b/src/com/sshtools/common/util/PropertyUtil.java @@ -0,0 +1,156 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + +import java.awt.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class PropertyUtil { + /** +* +* +* @param number +* @param defaultValue +* +* @return +*/ + public static int stringToInt(String number, int defaultValue) { + try { + return (number == null) ? defaultValue : Integer.parseInt(number); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** +* +* +* @param color +* +* @return +*/ + public static String colorToString(Color color) { + StringBuffer buf = new StringBuffer(); + buf.append('#'); + buf.append(numberToPaddedHexString(color.getRed(), 2)); + buf.append(numberToPaddedHexString(color.getGreen(), 2)); + buf.append(numberToPaddedHexString(color.getBlue(), 2)); + + return buf.toString(); + } + + /** +* +* +* @param font +* +* @return +*/ + public static String fontToString(Font font) { + StringBuffer b = new StringBuffer(font.getName()); + b.append(","); + b.append(font.getStyle()); + b.append(","); + b.append(font.getSize()); + + return b.toString(); + } + + /** +* +* +* @param fontString +* +* @return +*/ + public static Font stringToFont(String fontString) { + StringTokenizer st = new StringTokenizer(fontString, ","); + + try { + return new Font(st.nextToken(), Integer.parseInt(st.nextToken()), + Integer.parseInt(st.nextToken())); + } catch (Exception e) { + return null; + } + } + + /** +* +* +* @param s +* +* @return +* +* @throws IllegalArgumentException +*/ + public static Color stringToColor(String s) { + try { + return new Color(Integer.decode("0x" + s.substring(1, 3)).intValue(), + Integer.decode("0x" + s.substring(3, 5)).intValue(), + Integer.decode("0x" + s.substring(5, 7)).intValue()); + } catch (Exception e) { + throw new IllegalArgumentException( + "Bad color string format. Should be #rrggbb "); + } + } + + /** +* +* +* @param number +* @param size +* +* @return +* +* @throws IllegalArgumentException +*/ + public static String numberToPaddedHexString(int number, int size) { + String s = Integer.toHexString(number); + + if (s.length() > size) { + throw new IllegalArgumentException( + "Number too big for padded hex string"); + } + + StringBuffer buf = new StringBuffer(); + + for (int i = 0; i < (size - s.length()); i++) { + buf.append('0'); + } + + buf.append(s); + + return buf.toString(); + } +} diff --git a/src/com/sshtools/common/util/Search.java b/src/com/sshtools/common/util/Search.java new file mode 100644 index 0000000000000000000000000000000000000000..a29737a8774fa316c7830c7b2ceb59ca5c13a4a1 --- /dev/null +++ b/src/com/sshtools/common/util/Search.java @@ -0,0 +1,70 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.8 $ + */ +public class Search { + /** +* +* +* @param str +* @param query +* +* @return +* +* @throws IllegalArgumentException +*/ + public static boolean matchesWildcardQuery(String str, String query) + throws IllegalArgumentException { + int idx = query.indexOf("*"); + + if (idx > -1) { + // We have a wildcard search + if ((idx > 0) && (idx < (query.length() - 1))) { + throw new IllegalArgumentException( + "Wildcards not supported in middle of query string; use either 'searchtext*' or '*searchtext'"); + } + + if (idx == (query.length() - 1)) { + return str.startsWith(query.substring(0, idx)); + } else { + return str.endsWith(query.substring(idx + 1)); + } + } else { + if (str.equalsIgnoreCase(query)) { + return true; + } + } + + return false; + } +} diff --git a/src/com/sshtools/common/util/UID.java b/src/com/sshtools/common/util/UID.java new file mode 100644 index 0000000000000000000000000000000000000000..16c6bcb333e161472fb891fa04b901613f87ef66 --- /dev/null +++ b/src/com/sshtools/common/util/UID.java @@ -0,0 +1,172 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import java.io.ByteArrayInputStream; + +import java.security.DigestInputStream; +import java.security.MessageDigest; + +import java.util.Arrays; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.8 $ + */ +public class UID { + byte[] uid; + + private UID(String str) throws UIDException { + if (str == null) { + throw new UIDException("UID cannot be NULL"); + } + + try { + uid = new byte[str.length() / 2]; + + String tmp; + int pos = 0; + + for (int i = 0; i < str.length(); i += 2) { + tmp = str.substring(i, i + 2); + uid[pos++] = (byte) Integer.parseInt(tmp, 16); + } + } catch (NumberFormatException ex) { + throw new UIDException("Failed to parse UID String: " + + ex.getMessage()); + } + } + + private UID(byte[] uid) { + this.uid = uid; + } + + /** +* +* +* @param content +* +* @return +* +* @throws UIDException +*/ + public static UID generateUniqueId(byte[] content) + throws UIDException { + try { + // Create a uniqiue identifier from the content and some random data + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + + if (content != null) { + ByteArrayInputStream in = new ByteArrayInputStream(content); + DigestInputStream dis = new DigestInputStream(in, messageDigest); + + while (dis.read() != -1) { + ; + } + + dis.close(); + in.close(); + } + + // Add some random noise so the id is not generated soley by the + // file content + byte[] noise = new byte[1024]; + ConfigurationLoader.getRND().nextBytes(noise); + messageDigest.update(noise); + + // Generate the id + UID uid = new UID(messageDigest.digest()); + + return uid; + } catch (Exception ex) { + throw new UIDException("Failed to generate a unique identifier: " + + ex.getMessage()); + } + } + + /** +* +* +* @param uid +* +* @return +* +* @throws UIDException +*/ + public static UID fromString(String uid) throws UIDException { + return new UID(uid); + } + + /** +* +* +* @return +*/ + public String toString() { + StringBuffer checksumSb = new StringBuffer(); + + for (int i = 0; i < uid.length; i++) { + String hexStr = Integer.toHexString(0x00ff & uid[i]); + + if (hexStr.length() < 2) { + checksumSb.append("0"); + } + + checksumSb.append(hexStr); + } + + return checksumSb.toString(); + } + + /** +* +* +* @param obj +* +* @return +*/ + public boolean equals(Object obj) { + if ((obj != null) && obj instanceof UID) { + return Arrays.equals(uid, ((UID) obj).uid); + } + + return false; + } + + /** +* +* +* @return +*/ + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/src/com/sshtools/common/util/UIDException.java b/src/com/sshtools/common/util/UIDException.java new file mode 100644 index 0000000000000000000000000000000000000000..d6abf23a0719287d57fa28c3019e2d9f80dab5ba --- /dev/null +++ b/src/com/sshtools/common/util/UIDException.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.8 $ + */ +public class UIDException extends Exception { + /** +* Creates a new UIDException object. +* +* @param msg +*/ + public UIDException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/common/util/X11Util.java b/src/com/sshtools/common/util/X11Util.java new file mode 100644 index 0000000000000000000000000000000000000000..e475e0d8625f7eac6db89da22bc2336332ca8d16 --- /dev/null +++ b/src/com/sshtools/common/util/X11Util.java @@ -0,0 +1,146 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.common.util; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class X11Util { + // Logger + + /** */ + protected static Log log = LogFactory.getLog(X11Util.class); + static byte[] table = { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66 + }; + + /** +* +* +* @param displayNumber +* +* @return +* +* @throws IOException +*/ + public static String getCookie(int displayNumber) throws IOException { + log.debug("Getting cookie for " + displayNumber + " using xauth"); + + Process process = null; + InputStream in = null; + InputStream err = null; + OutputStream out = null; + + // try { + byte[] foo = new byte[16]; + ConfigurationLoader.getRND().nextBytes(foo); + + byte[] bar = new byte[32]; + + for (int i = 0; i < 16; i++) { + bar[2 * i] = table[(foo[i] >>> 4) & 0xf]; + bar[(2 * i) + 1] = table[(foo[i]) & 0xf]; + } + + return new String(bar); + + /* String cmd = "xauth list :" + displayNumber; +log.debug("Executing " + cmd); +process = Runtime.getRuntime().exec(cmd); +IOStreamConnector connect = new IOStreamConnector( +err = process.getErrorStream(), System.out); +BufferedReader reader = new BufferedReader( +new InputStreamReader(in = process.getInputStream())); +out = process.getOutputStream(); +String line = null; +String cookie = null; +while( ( line = reader.readLine() ) != null) { +log.debug(line); +StringTokenizer t = new StringTokenizer(line); +try { + String host = t.nextToken(); + String type = t.nextToken(); + String value = t.nextToken(); + if(cookie == null) { + cookie = value; + log.debug("Using cookie " + cookie); + } +} +catch(Exception e) { + log.error("Unexpected response from xauth.", e); +} +} +return cookie; + } + finally { +IOUtil.closeStream(in); +IOUtil.closeStream(err); +IOUtil.closeStream(out); + }*/ + } + + /** +* +* +* @param displayNumber +* +* @return +*/ + public static String createCookie(String displayNumber) { + log.error("Creating fake cookie"); + + StringBuffer b = new StringBuffer(); + + for (int i = 0; i < 16; i++) { + int r = (int) (Math.random() * 256); + String h = Integer.toHexString(r); + + if (h.length() == 1) { + b.append(0); + } + + b.append(h); + } + + log.error("Fake cookie is " + b.toString()); + + return b.toString(); + } +} diff --git a/src/com/sshtools/daemon/SshDaemon.java b/src/com/sshtools/daemon/SshDaemon.java new file mode 100644 index 0000000000000000000000000000000000000000..4295832bd769d8d3d66d0255f0a59644f5df09ac --- /dev/null +++ b/src/com/sshtools/daemon/SshDaemon.java @@ -0,0 +1,156 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon; + +import com.sshtools.common.configuration.*; + +import com.sshtools.daemon.configuration.*; +import com.sshtools.daemon.forwarding.*; +import com.sshtools.daemon.session.*; + +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.connection.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.net.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshDaemon { + private static Log log = LogFactory.getLog(SshDaemon.class); + + /** + * + * + * @param args + */ + public static void main(String[] args) { + try { + XmlServerConfigurationContext context = new XmlServerConfigurationContext(); + context.setServerConfigurationResource(ConfigurationLoader.checkAndGetProperty( + "sshtools.server", "server.xml")); + context.setPlatformConfigurationResource(System.getProperty( + "sshtools.platform", "platform.xml")); + ConfigurationLoader.initialize(false, context); + + XmlConfigurationContext context2 = new XmlConfigurationContext(); + context2.setFailOnError(false); + context2.setAPIConfigurationResource(ConfigurationLoader.checkAndGetProperty( + "sshtools.config", "sshtools.xml")); + context2.setAutomationConfigurationResource(ConfigurationLoader.checkAndGetProperty( + "sshtools.automate", "automation.xml")); + ConfigurationLoader.initialize(false, context2); + + if (args.length > 0) { + if (args[0].equals("-start")) { + start(); + } else if (args[0].equals("-stop")) { + if (args.length > 1) { + stop(args[1]); + } else { + stop("The framework daemon is shutting down"); + } + } else { + System.out.println("Usage: SshDaemon [-start|-stop]"); + } + } else { + System.out.println("Usage: SshDaemon [-start|-stop]"); + } + } catch (Exception e) { + log.error("The server failed to process the " + + ((args.length > 0) ? args[0] : "") + " command", e); + } + } + + /** + * + * + * @throws IOException + */ + public static void start() throws IOException { + // We need at least one host key + SshServer server = new SshServer() { + public void configureServices(ConnectionProtocol connection) + throws IOException { + connection.addChannelFactory(SessionChannelFactory.SESSION_CHANNEL, + new SessionChannelFactory()); + + if (ConfigurationLoader.isConfigurationAvailable( + ServerConfiguration.class)) { + if (((ServerConfiguration) ConfigurationLoader.getConfiguration( + ServerConfiguration.class)).getAllowTcpForwarding()) { + ForwardingServer forwarding = new ForwardingServer(connection); + } + } + } + + public void shutdown(String msg) { + // Disconnect all sessions + } + }; + + server.startServer(); + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + public static void stop(String msg) throws IOException { + try { + Socket socket = new Socket(InetAddress.getLocalHost(), + ((ServerConfiguration) ConfigurationLoader.getConfiguration( + ServerConfiguration.class)).getCommandPort()); + + // Write the command id + socket.getOutputStream().write(0x3a); + + // Write the length of the message (max 255) + int len = (msg.length() <= 255) ? msg.length() : 255; + socket.getOutputStream().write(len); + + // Write the message + if (len > 0) { + socket.getOutputStream().write(msg.substring(0, len).getBytes()); + } + + socket.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } +} diff --git a/src/com/sshtools/daemon/SshServer.java b/src/com/sshtools/daemon/SshServer.java new file mode 100644 index 0000000000000000000000000000000000000000..70ad6e538427ae637bd7df3c530d825695c17b05 --- /dev/null +++ b/src/com/sshtools/daemon/SshServer.java @@ -0,0 +1,394 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon; + +import com.sshtools.daemon.authentication.*; +import com.sshtools.daemon.configuration.*; +import com.sshtools.daemon.transport.*; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.connection.*; +import com.sshtools.j2ssh.net.*; +import com.sshtools.j2ssh.transport.*; +import com.sshtools.j2ssh.util.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.net.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public abstract class SshServer { + private static Log log = LogFactory.getLog(SshServer.class); + private ConnectionListener listener = null; + private ServerSocket server = null; + private boolean shutdown = false; + private ServerSocket commandServerSocket; + + /** */ + protected List activeConnections = new Vector(); + Thread thread; + + /** + * Creates a new SshServer object. + * + * @throws IOException + * @throws SshException + */ + public SshServer() throws IOException { + String serverId = System.getProperty("sshtools.serverid"); + + if (serverId != null) { + TransportProtocolServer.SOFTWARE_VERSION_COMMENTS = serverId; + } + + if (!ConfigurationLoader.isConfigurationAvailable( + ServerConfiguration.class)) { + throw new SshException("Server configuration not available!"); + } + + if (!ConfigurationLoader.isConfigurationAvailable( + PlatformConfiguration.class)) { + throw new SshException("Platform configuration not available"); + } + + if (((ServerConfiguration) ConfigurationLoader.getConfiguration( + ServerConfiguration.class)).getServerHostKeys().size() <= 0) { + throw new SshException( + "Server cannot start because there are no server host keys available"); + } + } + + /** + * + * + * @throws IOException + */ + public void startServer() throws IOException { + log.info("Starting server"); + shutdown = false; + startServerSocket(); + thread = new Thread(new Runnable() { + public void run() { + try { + startCommandSocket(); + } catch (IOException ex) { + log.info("Failed to start command socket", ex); + + try { + stopServer("The command socket failed to start"); + } catch (IOException ex1) { + } + } + } + }); + thread.start(); + + try { + thread.join(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * + * + * @param command + * @param client + * + * @throws IOException + */ + protected void processCommand(int command, Socket client) + throws IOException { + if (command == 0x3a) { + int len = client.getInputStream().read(); + byte[] msg = new byte[len]; + client.getInputStream().read(msg); + stopServer(new String(msg)); + } + } + + /** + * + * + * @throws IOException + */ + protected void startCommandSocket() throws IOException { + try { + commandServerSocket = new ServerSocket(((ServerConfiguration) ConfigurationLoader.getConfiguration( + ServerConfiguration.class)).getCommandPort(), 50, + InetAddress.getLocalHost()); + + Socket client; + + while ((client = commandServerSocket.accept()) != null) { + log.info("Command request received"); + + // Read and process the command + processCommand(client.getInputStream().read(), client); + client.close(); + + if (shutdown) { + break; + } + } + + commandServerSocket.close(); + } catch (Exception e) { + if (!shutdown) { + log.fatal("The command socket failed", e); + } + } + } + + /** + * + * + * @throws IOException + */ + protected void startServerSocket() throws IOException { + listener = new ConnectionListener(((ServerConfiguration) ConfigurationLoader.getConfiguration( + ServerConfiguration.class)).getListenAddress(), + ((ServerConfiguration) ConfigurationLoader.getConfiguration( + ServerConfiguration.class)).getPort()); + listener.start(); + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + public void stopServer(String msg) throws IOException { + log.info("Shutting down server"); + shutdown = true; + log.debug(msg); + shutdown(msg); + listener.stop(); + log.debug("Stopping command server"); + + try { + if (commandServerSocket != null) { + commandServerSocket.close(); + } + } catch (IOException ioe) { + log.error(ioe); + } + } + + /** + * + * + * @param msg + */ + protected abstract void shutdown(String msg); + + /** + * + * + * @param connection + * + * @throws IOException + */ + protected abstract void configureServices(ConnectionProtocol connection) + throws IOException; + + /** + * + * + * @param socket + * + * @throws IOException + */ + protected void refuseSession(Socket socket) throws IOException { + TransportProtocolServer transport = new TransportProtocolServer(true); + transport.startTransportProtocol(new ConnectedSocketTransportProvider( + socket), new SshConnectionProperties()); + } + + /** + * + * + * @param socket + * + * @return + * + * @throws IOException + */ + protected TransportProtocolServer createSession(Socket socket) + throws IOException { + log.debug("Initializing connection"); + + InetAddress address = socket.getInetAddress(); + + /*( (InetSocketAddress) socket + .getRemoteSocketAddress()).getAddress();*/ + log.debug("Remote Hostname: " + address.getHostName()); + log.debug("Remote IP: " + address.getHostAddress()); + + TransportProtocolServer transport = new TransportProtocolServer(); + + // Create the Authentication Protocol + AuthenticationProtocolServer authentication = new AuthenticationProtocolServer(); + + // Create the Connection Protocol + ConnectionProtocol connection = new ConnectionProtocol(); + + // Configure the connections services + configureServices(connection); + + // Allow the Connection Protocol to be accepted by the Authentication Protocol + authentication.acceptService(connection); + + // Allow the Authentication Protocol to be accepted by the Transport Protocol + transport.acceptService(authentication); + transport.startTransportProtocol(new ConnectedSocketTransportProvider( + socket), new SshConnectionProperties()); + + return transport; + } + + class ConnectionListener implements Runnable { + private Log log = LogFactory.getLog(ConnectionListener.class); + private ServerSocket server; + private String listenAddress; + private Thread thread; + private int maxConnections; + private int port; + private StartStopState state = new StartStopState(StartStopState.STOPPED); + + public ConnectionListener(String listenAddress, int port) { + this.port = port; + this.listenAddress = listenAddress; + } + + public void run() { + try { + log.debug("Starting connection listener thread"); + state.setValue(StartStopState.STARTED); + server = new ServerSocket(port); + + Socket socket; + maxConnections = ((ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class)).getMaxConnections(); + + boolean refuse = false; + TransportProtocolEventHandler eventHandler = new TransportProtocolEventAdapter() { + public void onDisconnect(TransportProtocol transport) { + // Remove from our active channels list only if + // were still connected (the thread cleans up + // when were exiting so this is to avoid any concurrent + // modification problems + if (state.getValue() != StartStopState.STOPPED) { + synchronized (activeConnections) { + log.info(transport.getUnderlyingProviderDetail() + + " connection closed"); + activeConnections.remove(transport); + } + } + } + }; + + try { + while (((socket = server.accept()) != null) && + (state.getValue() == StartStopState.STARTED)) { + log.debug("New connection requested"); + + if (maxConnections < activeConnections.size()) { + refuseSession(socket); + } else { + TransportProtocolServer transport = createSession(socket); + log.info("Monitoring active session from " + + socket.getInetAddress().getHostName()); + + synchronized (activeConnections) { + activeConnections.add(transport); + } + + transport.addEventHandler(eventHandler); + } + } + } catch (IOException ex) { + if (state.getValue() != StartStopState.STOPPED) { + log.info("The server was shutdown unexpectedly", ex); + } + } + + state.setValue(StartStopState.STOPPED); + + // Closing all connections + log.info("Disconnecting active sessions"); + + for (Iterator it = activeConnections.iterator(); it.hasNext();) { + ((TransportProtocolServer) it.next()).disconnect( + "The server is shuting down"); + } + + listener = null; + log.info("Exiting connection listener thread"); + } catch (IOException ex) { + log.info("The server thread failed", ex); + } finally { + thread = null; + } + + // brett + // System.exit(0); + } + + public void start() { + thread = new SshThread(this, "Connection listener", true); + thread.start(); + } + + public void stop() { + try { + state.setValue(StartStopState.STOPPED); + server.close(); + + if (thread != null) { + thread.interrupt(); + } + } catch (IOException ioe) { + log.warn("The listening socket reported an error during shutdown", + ioe); + } + } + } +} diff --git a/src/com/sshtools/daemon/authentication/AuthenticationProtocolServer.java b/src/com/sshtools/daemon/authentication/AuthenticationProtocolServer.java new file mode 100644 index 0000000000000000000000000000000000000000..69bc125619d478a08584e3f6be4d1deb09351c3d --- /dev/null +++ b/src/com/sshtools/daemon/authentication/AuthenticationProtocolServer.java @@ -0,0 +1,346 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import com.sshtools.daemon.configuration.*; +import com.sshtools.daemon.platform.*; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.authentication.*; +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.transport.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class AuthenticationProtocolServer extends AsyncService { + private static Log log = LogFactory.getLog(AuthenticationProtocolServer.class); + private List completedAuthentications = new ArrayList(); + private Map acceptServices = new HashMap(); + private List availableAuths; + private String serviceToStart; + private int[] messageFilter = new int[1]; + private SshMessageStore methodMessages = new SshMessageStore(); + private int attempts = 0; + private boolean completed = false; + + /** + * Creates a new AuthenticationProtocolServer object. + */ + public AuthenticationProtocolServer() { + super("ssh-userauth"); + messageFilter[0] = SshMsgUserAuthRequest.SSH_MSG_USERAUTH_REQUEST; + } + + /** + * + * + * @throws java.io.IOException + */ + protected void onServiceAccept() throws java.io.IOException { + } + + /** + * + * + * @param startMode + * + * @throws java.io.IOException + */ + protected void onServiceInit(int startMode) throws java.io.IOException { + // Register the required messages + messageStore.registerMessage(SshMsgUserAuthRequest.SSH_MSG_USERAUTH_REQUEST, + SshMsgUserAuthRequest.class); + transport.addMessageStore(methodMessages); + } + + /** + * + * + * @return + */ + public byte[] getSessionIdentifier() { + return transport.getSessionIdentifier(); + } + + /** + * + * + * @return + */ + public TransportProtocolState getConnectionState() { + return transport.getState(); + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + public void sendMessage(SshMessage msg) throws IOException { + transport.sendMessage(msg, this); + } + + /** + * + * + * @return + * + * @throws IOException + * @throws SshException + */ + public SshMessage readMessage() throws IOException { + try { + return methodMessages.nextMessage(); + } catch (InterruptedException ex) { + throw new SshException("The thread was interrupted"); + } + } + + /** + * + * + * @param messageId + * @param cls + */ + public void registerMessage(int messageId, Class cls) { + methodMessages.registerMessage(messageId, cls); + } + + /** + * + * + * @throws java.io.IOException + * @throws AuthenticationProtocolException + */ + protected void onServiceRequest() throws java.io.IOException { + // Send a user auth banner if configured + ServerConfiguration server = (ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class); + + if (server == null) { + throw new AuthenticationProtocolException( + "Server configuration unavailable"); + } + + availableAuths = new ArrayList(); + + Iterator it = SshAuthenticationServerFactory.getSupportedMethods() + .iterator(); + String method; + List allowed = server.getAllowedAuthentications(); + + while (it.hasNext()) { + method = (String) it.next(); + + if (allowed.contains(method)) { + availableAuths.add(method); + } + } + + if (availableAuths.size() <= 0) { + throw new AuthenticationProtocolException( + "No valid authentication methods have been specified"); + } + + // Accept the service request + sendServiceAccept(); + + String bannerFile = server.getAuthenticationBanner(); + + if (bannerFile != null) { + if (bannerFile.length() > 0) { + InputStream in = ConfigurationLoader.loadFile(bannerFile); + + if (in != null) { + byte[] data = new byte[in.available()]; + in.read(data); + in.close(); + + SshMsgUserAuthBanner bannerMsg = new SshMsgUserAuthBanner(new String( + data)); + transport.sendMessage(bannerMsg, this); + } else { + log.info("The banner file '" + bannerFile + + "' was not found"); + } + } + } + } + + /** + * + * + * @param msg + * + * @throws java.io.IOException + * @throws AuthenticationProtocolException + */ + protected void onMessageReceived(SshMessage msg) throws java.io.IOException { + switch (msg.getMessageId()) { + case SshMsgUserAuthRequest.SSH_MSG_USERAUTH_REQUEST: { + onMsgUserAuthRequest((SshMsgUserAuthRequest) msg); + + break; + } + + default: + throw new AuthenticationProtocolException( + "Unregistered message received!"); + } + } + + /** + * + * + * @return + */ + protected int[] getAsyncMessageFilter() { + return messageFilter; + } + + /** + * + * + * @param service + */ + public void acceptService(Service service) { + acceptServices.put(service.getServiceName(), service); + } + + private void sendUserAuthFailure(boolean success) throws IOException { + Iterator it = availableAuths.iterator(); + String auths = null; + + while (it.hasNext()) { + auths = ((auths == null) ? "" : (auths + ",")) + + (String) it.next(); + } + + SshMsgUserAuthFailure reply = new SshMsgUserAuthFailure(auths, success); + transport.sendMessage(reply, this); + } + + /** + * + */ + protected void onStop() { + try { + // If authentication succeeded then wait for the + // disconnect and logoff the user + if (completed) { + try { + transport.getState().waitForState(TransportProtocolState.DISCONNECTED); + } catch (InterruptedException ex) { + log.warn("The authentication service was interrupted"); + } + + NativeAuthenticationProvider nap = NativeAuthenticationProvider.getInstance(); + nap.logoffUser(); + } + } catch (IOException ex) { + log.warn("Failed to logoff " + SshThread.getCurrentThreadUser()); + } + } + + private void sendUserAuthSuccess() throws IOException { + SshMsgUserAuthSuccess msg = new SshMsgUserAuthSuccess(); + Service service = (Service) acceptServices.get(serviceToStart); + service.init(Service.ACCEPTING_SERVICE, transport); //, nativeSettings); + service.start(); + transport.sendMessage(msg, this); + completed = true; + stop(); + } + + private void onMsgUserAuthRequest(SshMsgUserAuthRequest msg) + throws IOException { + if (msg.getMethodName().equals("none")) { + sendUserAuthFailure(false); + } else { + if (attempts >= ((ServerConfiguration) ConfigurationLoader.getConfiguration( + ServerConfiguration.class)).getMaxAuthentications()) { + // Too many authentication attempts + transport.disconnect("Too many failed authentication attempts"); + } else { + // If the service is supported then perfrom the authentication + if (acceptServices.containsKey(msg.getServiceName())) { + String method = msg.getMethodName(); + + if (availableAuths.contains(method)) { + SshAuthenticationServer auth = SshAuthenticationServerFactory.newInstance(method); + serviceToStart = msg.getServiceName(); + + //auth.setUsername(msg.getUsername()); + int result = auth.authenticate(this, msg); //, nativeSettings); + + if (result == AuthenticationProtocolState.FAILED) { + sendUserAuthFailure(false); + } else if (result == AuthenticationProtocolState.COMPLETE) { + completedAuthentications.add(auth.getMethodName()); + + ServerConfiguration sc = (ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class); + Iterator it = sc.getRequiredAuthentications() + .iterator(); + + while (it.hasNext()) { + if (!completedAuthentications.contains( + it.next())) { + sendUserAuthFailure(true); + + return; + } + } + + thread.setUsername(msg.getUsername()); + sendUserAuthSuccess(); + } else { + // Authentication probably returned READY as no completion + // evaluation was needed + } + } else { + sendUserAuthFailure(false); + } + } else { + sendUserAuthFailure(false); + } + + attempts++; + } + } + } +} diff --git a/src/com/sshtools/daemon/authentication/AuthorizationFileVerification.java b/src/com/sshtools/daemon/authentication/AuthorizationFileVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..97458709b23d32ec94aae3312c323902301553dc --- /dev/null +++ b/src/com/sshtools/daemon/authentication/AuthorizationFileVerification.java @@ -0,0 +1,224 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import com.sshtools.common.configuration.*; + +import com.sshtools.daemon.configuration.*; +import com.sshtools.daemon.platform.*; + +import com.sshtools.j2ssh.authentication.*; +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.transport.publickey.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class AuthorizationFileVerification implements PublicKeyVerification { + private static Log log = LogFactory.getLog(AuthorizationFileVerification.class); + + /** + * + * + * @param username + * @param algorithm + * @param encoded + * @param service + * @param sessionId + * @param signature + * + * @return + * + * @throws IOException + */ + public boolean verifyKeySignature(String username, String algorithm, + byte[] encoded, String service, byte[] sessionId, byte[] signature) + throws IOException { + try { + SshPublicKey key = getAuthorizedKey(username, algorithm, encoded); + ByteArrayWriter data = new ByteArrayWriter(); + data.writeBinaryString(sessionId); + data.write(SshMsgUserAuthRequest.SSH_MSG_USERAUTH_REQUEST); + data.writeString(username); + data.writeString(service); + data.writeString("publickey"); + data.write(1); + data.writeString(key.getAlgorithmName()); + data.writeBinaryString(key.getEncoded()); + + if (key.verifySignature(signature, data.toByteArray())) { + return true; + } + } catch (IOException ex) { + } + + return false; + } + + private SshPublicKey getAuthorizedKey(String username, String algorithm, + byte[] encoded) throws IOException { + NativeAuthenticationProvider provider = NativeAuthenticationProvider.getInstance(); + String userHome = provider.getHomeDirectory(username); //, nativeSettings); + + if (userHome == null) { + log.warn("There is no home directory for " + username + + " is available"); + } + + // Replace '\' with '/' because when we use it in String.replaceAll + // for some reason it removes them? + if (userHome != null) { + userHome = userHome.replace('\\', '/'); + } + + ServerConfiguration config = (ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class); + String authorizationFile; + String userConfigDir = config.getUserConfigDirectory(); + + // First replace any '\' with '/' (Becasue replaceAll removes them!) + userConfigDir = userConfigDir.replace('\\', '/'); + + // Replace any home directory tokens + if ((userConfigDir.indexOf("%D") > -1) && (userHome == null)) { + throw new IOException( + "<UserConfigDirectory> requires home directory, but none available for " + + username); + } + + int idx = 0; + + while ((idx = userConfigDir.indexOf("%D", idx + 1)) > -1) { + StringBuffer buf = new StringBuffer(userConfigDir); + buf = buf.replace(idx, idx + 1, userHome); + userConfigDir = buf.toString(); + } + + idx = 0; + + while ((idx = userConfigDir.indexOf("%U", idx + 1)) > -1) { + StringBuffer buf = new StringBuffer(userConfigDir); + buf = buf.replace(idx, idx + 1, username); + userConfigDir = buf.toString(); + } + + // Replace the '/' with File.seperator and trim + userConfigDir = userConfigDir.replace('/', File.separatorChar).trim(); + + if (!userConfigDir.endsWith(File.separator)) { + userConfigDir += File.separator; + } + + authorizationFile = userConfigDir + config.getAuthorizationFile(); + + // Load the authorization file + File file = new File(authorizationFile); + + if (!file.exists()) { + log.info("authorizationFile: " + authorizationFile + + " does not exist."); + throw new IOException("authorizationFile: " + authorizationFile + + " does not exist."); + } + + FileInputStream in = new FileInputStream(file); + Authorization keys; + + try { + keys = new Authorization(in); + } catch (Exception e) { + throw new AuthenticationProtocolException( + "Failed to load authorized keys file " + authorizationFile); + } + + // SshPublicKey key = SshPublicKeyFile.parse(encoded); + Iterator it = keys.getAuthorizedKeys().iterator(); + SshKeyPair pair = SshKeyPairFactory.newInstance(algorithm); + SshPublicKey authorizedKey = null; + SshPublicKey key = pair.decodePublicKey(encoded); + boolean valid = false; + String keyfile; + + while (it.hasNext()) { + keyfile = (String) it.next(); + + // Look for the file in the user config dir first + file = new File(userConfigDir + keyfile); + + // If it does not exist then look absolute + if (!file.exists()) { + file = new File(keyfile); + } + + if (file.exists()) { + // Try to open the public key in the default file format + // otherwise attempt the supported key formats + SshPublicKeyFile pkf = SshPublicKeyFile.parse(file); + authorizedKey = pkf.toPublicKey(); + + if (authorizedKey.equals(key)) { + return authorizedKey; + } + } else { + log.info("Failed attempt to load key file " + keyfile); + } + } + + throw new IOException(""); + } + + /** + * + * + * @param username + * @param algorithm + * @param encoded + * + * @return + * + * @throws IOException + */ + public boolean acceptKey(String username, String algorithm, byte[] encoded) + throws IOException { + try { + getAuthorizedKey(username, algorithm, encoded); + + return true; + } catch (Exception ex) { + return false; + } + } +} diff --git a/src/com/sshtools/daemon/authentication/KBIPasswordAuthenticationServer.java b/src/com/sshtools/daemon/authentication/KBIPasswordAuthenticationServer.java new file mode 100644 index 0000000000000000000000000000000000000000..6dfff398f78725d82e57ad3fa456096516c9207f --- /dev/null +++ b/src/com/sshtools/daemon/authentication/KBIPasswordAuthenticationServer.java @@ -0,0 +1,163 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import com.sshtools.daemon.platform.*; + +import com.sshtools.j2ssh.authentication.*; +import com.sshtools.j2ssh.transport.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.10 $ + */ +public class KBIPasswordAuthenticationServer extends SshAuthenticationServer { + private static Log log = LogFactory.getLog(KBIPasswordAuthenticationServer.class); + + /** + * + * + * @return + */ + public final String getMethodName() { + return "keyboard-interactive"; + } + + /** + * + * + * @param tokens + */ + public void setAuthenticatedTokens(Map tokens) { + } + + /** + * + * + * @param authentication + * @param msg + * + * @return + * + * @throws IOException + */ + public int authenticate(AuthenticationProtocolServer authentication, + SshMsgUserAuthRequest msg) throws IOException { //, Map nativeSettings) + + NativeAuthenticationProvider authImpl = NativeAuthenticationProvider.getInstance(); + + if (authImpl == null) { + log.error( + "Cannot perfrom authentication witout native authentication provider"); + + return AuthenticationProtocolState.FAILED; + } + + authentication.registerMessage(SshMsgUserAuthInfoResponse.SSH_MSG_USERAUTH_INFO_RESPONSE, + SshMsgUserAuthInfoResponse.class); + + SshMsgUserAuthInfoRequest info = new SshMsgUserAuthInfoRequest("Password authentication", + "", ""); + info.addPrompt(msg.getUsername() + "'s password", false); + authentication.sendMessage(info); + + SshMessage response = authentication.readMessage(); + + if (response instanceof SshMsgUserAuthInfoResponse) { + String[] responses = ((SshMsgUserAuthInfoResponse) response).getResponses(); + + if (responses.length == 1) { + String password = responses[0]; + + try { + if (authImpl.logonUser(msg.getUsername(), password)) { //, nativeSettings)) { + log.info(msg.getUsername() + + " has passed password authentication"); + + return AuthenticationProtocolState.COMPLETE; + } else { + log.info(msg.getUsername() + + " has failed password authentication"); + + return AuthenticationProtocolState.FAILED; + } + } catch (PasswordChangeException ex) { + info = new SshMsgUserAuthInfoRequest("Password change required", + "", ""); + info.addPrompt("New password", false); + info.addPrompt("Confirm password", false); + authentication.sendMessage(info); + response = authentication.readMessage(); + + if (response instanceof SshMsgUserAuthInfoResponse) { + responses = ((SshMsgUserAuthInfoResponse) response).getResponses(); + + if (responses.length == 2) { + if (responses[0].equals(responses[1])) { + if (authImpl.changePassword(msg.getUsername(), + password, responses[0])) { + return AuthenticationProtocolState.COMPLETE; + } else { + return AuthenticationProtocolState.FAILED; + } + } else { + return AuthenticationProtocolState.FAILED; + } + } else { + log.error("Client replied with an invalid message " + + response.getMessageName()); + + return AuthenticationProtocolState.FAILED; + } + } else { + log.error("Client replied with an invalid message " + + response.getMessageName()); + + return AuthenticationProtocolState.FAILED; + } + } + } else { + log.error("Client responded with too many values!"); + + return AuthenticationProtocolState.FAILED; + } + } else { + log.error("Client replied with an invalid message " + + response.getMessageName()); + + return AuthenticationProtocolState.FAILED; + } + } +} diff --git a/src/com/sshtools/daemon/authentication/PasswordAuthenticationServer.java b/src/com/sshtools/daemon/authentication/PasswordAuthenticationServer.java new file mode 100644 index 0000000000000000000000000000000000000000..214579523c7d26002e4d97ffe865f8fa5b7dbb0b --- /dev/null +++ b/src/com/sshtools/daemon/authentication/PasswordAuthenticationServer.java @@ -0,0 +1,131 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import com.sshtools.daemon.platform.*; + +import com.sshtools.j2ssh.authentication.*; +import com.sshtools.j2ssh.io.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.10 $ + */ +public class PasswordAuthenticationServer extends SshAuthenticationServer { + private static Log log = LogFactory.getLog(PasswordAuthenticationServer.class); + + /** + * + * + * @return + */ + public final String getMethodName() { + return "password"; + } + + /** + * + * + * @param tokens + */ + public void setAuthenticatedTokens(Map tokens) { + } + + /** + * + * + * @param authentication + * @param msg + * + * @return + * + * @throws IOException + */ + public int authenticate(AuthenticationProtocolServer authentication, + SshMsgUserAuthRequest msg) throws IOException { + NativeAuthenticationProvider authImpl = NativeAuthenticationProvider.getInstance(); + + if (authImpl == null) { + log.error( + "Cannot perfrom authentication witout native authentication provider"); + + return AuthenticationProtocolState.FAILED; + } + + ByteArrayReader bar = new ByteArrayReader(msg.getRequestData()); + boolean changepwd = ((bar.read() == 0) ? false : true); + String password = bar.readString(); + String newpassword = null; + + if (changepwd) { + newpassword = bar.readString(); + + try { + if (!authImpl.changePassword(msg.getUsername(), password, + newpassword)) { + return AuthenticationProtocolState.FAILED; + } + + if (authImpl.logonUser(msg.getUsername(), newpassword)) { + return AuthenticationProtocolState.COMPLETE; + } else { + return AuthenticationProtocolState.FAILED; + } + } catch (PasswordChangeException ex1) { + return AuthenticationProtocolState.FAILED; + } + } else { + try { + if (authImpl.logonUser(msg.getUsername(), password)) { + log.info(msg.getUsername() + + " has passed password authentication"); + + return AuthenticationProtocolState.COMPLETE; + } else { + log.info(msg.getUsername() + + " has failed password authentication"); + + return AuthenticationProtocolState.FAILED; + } + } catch (PasswordChangeException ex) { + SshMsgUserAuthPwdChangeReq reply = new SshMsgUserAuthPwdChangeReq(msg.getUsername() + + " is required to change password", ""); + authentication.sendMessage(reply); + + return AuthenticationProtocolState.READY; + } + } + } +} diff --git a/src/com/sshtools/daemon/authentication/PublicKeyAuthenticationServer.java b/src/com/sshtools/daemon/authentication/PublicKeyAuthenticationServer.java new file mode 100644 index 0000000000000000000000000000000000000000..e2f16ce6e66a4b5f0f90f5c6943efdf20c69a1d6 --- /dev/null +++ b/src/com/sshtools/daemon/authentication/PublicKeyAuthenticationServer.java @@ -0,0 +1,145 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import com.sshtools.daemon.platform.*; + +import com.sshtools.j2ssh.authentication.*; +import com.sshtools.j2ssh.io.*; + +import org.apache.commons.logging.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class PublicKeyAuthenticationServer extends SshAuthenticationServer { + private static Class pkv = AuthorizationFileVerification.class; + private Log log = LogFactory.getLog(PublicKeyAuthenticationServer.class); + + /** + * Creates a new PublicKeyAuthenticationServer object. + */ + public PublicKeyAuthenticationServer() { + } + + /** + * + * + * @return + */ + public String getMethodName() { + return "publickey"; + } + + /** + * + * + * @param pkv + */ + public static void setVerificationImpl(Class pkv) { + PublicKeyAuthenticationServer.pkv = pkv; + } + + /** + * + * + * @param authentication + * @param msg + * + * @return + * + * @throws IOException + */ + public int authenticate(AuthenticationProtocolServer authentication, + SshMsgUserAuthRequest msg) throws IOException { //, Map nativeSettings) + + ByteArrayReader bar = new ByteArrayReader(msg.getRequestData()); + + // If check == 0 then authenticate, otherwise just inform that + // the authentication can continue with the key supplied + int check = bar.read(); + String algorithm = bar.readString(); + byte[] encoded = bar.readBinaryString(); + byte[] signature = null; + + try { + PublicKeyVerification verify = (PublicKeyVerification) pkv.newInstance(); + + if (check == 0) { + // Verify that the public key can be used for authenticaiton + //boolean ok = SshKeyPairFactory.supportsKey(algorithm); + // Send the reply + if (verify.acceptKey(msg.getUsername(), algorithm, encoded)) { + SshMsgUserAuthPKOK reply = new SshMsgUserAuthPKOK(algorithm, + encoded); + authentication.sendMessage(reply); + + return AuthenticationProtocolState.READY; + } else { + return AuthenticationProtocolState.FAILED; + } + } else { + signature = bar.readBinaryString(); + + NativeAuthenticationProvider authProv = NativeAuthenticationProvider.getInstance(); + + if (authProv == null) { + log.error( + "Authentication failed because no native authentication provider is available"); + + return AuthenticationProtocolState.FAILED; + } + + if (!authProv.logonUser(msg.getUsername())) { //, nativeSettings)) { + log.info("Authentication failed because " + + msg.getUsername() + " is not a valid username"); + + return AuthenticationProtocolState.FAILED; + } + + try { + if (verify.verifyKeySignature(msg.getUsername(), algorithm, + encoded, msg.getServiceName(), + authentication.getSessionIdentifier(), signature)) { + return AuthenticationProtocolState.COMPLETE; + } + } catch (Exception ex) { + log.error("Failed to create an instance of the verification implementation", + ex); + } + } + } catch (Exception e) { + } + + return AuthenticationProtocolState.FAILED; + } +} diff --git a/src/com/sshtools/daemon/authentication/PublicKeyVerification.java b/src/com/sshtools/daemon/authentication/PublicKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..d25ba5c60c3a5b69eec7a6a51e7785815aec75ef --- /dev/null +++ b/src/com/sshtools/daemon/authentication/PublicKeyVerification.java @@ -0,0 +1,69 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface PublicKeyVerification { + /** + * + * + * @param username + * @param algorithm + * @param encoded + * @param service + * @param sessionId + * @param signature + * + * @return + * + * @throws IOException + */ + public boolean verifyKeySignature(String username, String algorithm, + byte[] encoded, String service, byte[] sessionId, byte[] signature) + throws IOException; + + /** + * + * + * @param username + * @param algorithm + * @param encoded + * + * @return + * + * @throws IOException + */ + public boolean acceptKey(String username, String algorithm, byte[] encoded) + throws IOException; +} diff --git a/src/com/sshtools/daemon/authentication/SshAuthenticationServer.java b/src/com/sshtools/daemon/authentication/SshAuthenticationServer.java new file mode 100644 index 0000000000000000000000000000000000000000..3d97ea8e7d9fe9b35b65f079746fa2e07c213395 --- /dev/null +++ b/src/com/sshtools/daemon/authentication/SshAuthenticationServer.java @@ -0,0 +1,69 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import com.sshtools.j2ssh.authentication.SshMsgUserAuthRequest; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.10 $ + */ +public abstract class SshAuthenticationServer { + private String username; + + /** + * + * + * @return + */ + public abstract String getMethodName(); + + /** + * + * + * @param authentication + * @param msg + * + * @return + * + * @throws IOException + */ + public abstract int authenticate( + AuthenticationProtocolServer authentication, SshMsgUserAuthRequest msg) + throws IOException; + + // public void setUsername(String username) { + // this.username = username; + // } + // public String getUsername() { + // return username; + // } +} diff --git a/src/com/sshtools/daemon/authentication/SshAuthenticationServerFactory.java b/src/com/sshtools/daemon/authentication/SshAuthenticationServerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..75658f66afc75b3b21f303e21c13187ec57c1698 --- /dev/null +++ b/src/com/sshtools/daemon/authentication/SshAuthenticationServerFactory.java @@ -0,0 +1,152 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.authentication; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.ExtensionAlgorithm; +import com.sshtools.j2ssh.configuration.SshAPIConfiguration; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.10 $ + */ +public class SshAuthenticationServerFactory { + private static Map auths; + private static Log log = LogFactory.getLog(SshAuthenticationServerFactory.class); + + /** */ + public final static String AUTH_PASSWORD = "password"; + + /** */ + public final static String AUTH_PK = "publickey"; + + /** */ + public final static String AUTH_KBI = "keyboard-interactive"; + + static { + auths = new HashMap(); + log.info("Loading supported authentication methods"); + auths.put(AUTH_PASSWORD, PasswordAuthenticationServer.class); + auths.put(AUTH_PK, PublicKeyAuthenticationServer.class); + auths.put(AUTH_KBI, KBIPasswordAuthenticationServer.class); + + try { + if (ConfigurationLoader.isConfigurationAvailable( + SshAPIConfiguration.class)) { + SshAPIConfiguration config = (SshAPIConfiguration) ConfigurationLoader.getConfiguration(SshAPIConfiguration.class); + List addons = config.getAuthenticationExtensions(); + Iterator it = addons.iterator(); + + // Add the methods to our supported list + while (it.hasNext()) { + ExtensionAlgorithm method = (ExtensionAlgorithm) it.next(); + String name = method.getAlgorithmName(); + + if (auths.containsKey(name)) { + log.debug("Standard authentication implementation for " + + name + " is being overidden by " + + method.getImplementationClass()); + } else { + log.debug(name + " authentication is implemented by " + + method.getImplementationClass()); + } + + try { + Class cls = ConfigurationLoader.getExtensionClass(method.getImplementationClass()); + Object obj = cls.newInstance(); + + if (obj instanceof SshAuthenticationServer) { + auths.put(name, cls); + } + } catch (Exception e) { + log.warn( + "Failed to load extension authentication implementation " + + method.getImplementationClass(), e); + } + } + } + } catch (ConfigurationException ex) { + } + } + + /** + * Creates a new SshAuthenticationServerFactory object. + */ + protected SshAuthenticationServerFactory() { + } + + /** + * + */ + public static void initialize() { + } + + /** + * + * + * @return + */ + public static List getSupportedMethods() { + // Get the list of ciphers + ArrayList list = new ArrayList(auths.keySet()); + + // Return the list + return list; + } + + /** + * + * + * @param methodName + * + * @return + * + * @throws AlgorithmNotSupportedException + */ + public static SshAuthenticationServer newInstance(String methodName) + throws AlgorithmNotSupportedException { + try { + return (SshAuthenticationServer) ((Class) auths.get(methodName)).newInstance(); + } catch (Exception e) { + throw new AlgorithmNotSupportedException(methodName + + " is not supported!"); + } + } +} diff --git a/src/com/sshtools/daemon/configuration/AllowedSubsystem.java b/src/com/sshtools/daemon/configuration/AllowedSubsystem.java new file mode 100644 index 0000000000000000000000000000000000000000..8e81b4e7506f807d366c01204a50eeeaa685cb3a --- /dev/null +++ b/src/com/sshtools/daemon/configuration/AllowedSubsystem.java @@ -0,0 +1,79 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.configuration; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class AllowedSubsystem { + private String type; + private String name; + private String provider; + + /** + * Creates a new AllowedSubsystem object. + * + * @param type + * @param name + * @param provider + */ + public AllowedSubsystem(String type, String name, String provider) { + this.type = type; + this.name = name; + this.provider = provider; + } + + /** + * + * + * @return + */ + public String getType() { + return type; + } + + /** + * + * + * @return + */ + public String getName() { + return name; + } + + /** + * + * + * @return + */ + public String getProvider() { + return provider; + } +} diff --git a/src/com/sshtools/daemon/configuration/PlatformConfiguration.java b/src/com/sshtools/daemon/configuration/PlatformConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..19b7aa5470e90bd38b850577e9a82c8f74292779 --- /dev/null +++ b/src/com/sshtools/daemon/configuration/PlatformConfiguration.java @@ -0,0 +1,422 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.configuration; + +import com.sshtools.daemon.vfs.*; + +import org.apache.commons.logging.*; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import java.io.*; + +import java.util.*; + +import javax.xml.parsers.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class PlatformConfiguration extends DefaultHandler { + private static Log log = LogFactory.getLog(PlatformConfiguration.class); + private static final String PLATFORM_ELEMENT = "PlatformConfiguration"; + private static final String NATIVE_PROCESS_ELEMENT = "NativeProcessProvider"; + private static final String NATIVE_AUTH_ELEMENT = "NativeAuthenticationProvider"; + private static final String NFS_ELEMENT = "NativeFileSystemProvider"; + private static final String NATIVE_SETTING_ELEMENT = "NativeSetting"; + private static final String VFSMOUNT_ELEMENT = "VFSMount"; + private static final String VFSROOT_ELEMENT = "VFSRoot"; + private static final String VFSPERMISSION_ELEMENT = "VFSPermission"; + private static final String PATH_ATTRIBUTE = "path"; + private static final String MOUNT_ATTRIBUTE = "mount"; + private static final String NAME_ATTRIBUTE = "name"; + private static final String VALUE_ATTRIBUTE = "value"; + private static final String PERMISSIONS_ATTRIBUTE = "permissions"; + private String currentElement = null; + private Map nativeSettings = new HashMap(); + private String nativeProcessProvider = null; + private String nativeAuthenticationProvider = null; + private String nativeFileSystemProvider = null; + private Map vfsMounts = new HashMap(); + private VFSMount vfsRoot = null; + + /** + * Creates a new PlatformConfiguration object. + * + * @param in + * + * @throws SAXException + * @throws ParserConfigurationException + * @throws IOException + */ + protected PlatformConfiguration(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + reload(in); + } + + /** + * + * + * @param in + * + * @throws SAXException + * @throws ParserConfigurationException + * @throws IOException + */ + public void reload(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + saxParser.parse(in, new PlatformConfigurationSAXHandler()); + } + + /** + * + * + * @return + */ + public Map getVFSMounts() { + return vfsMounts; + } + + /** + * + * + * @return + */ + public String getNativeAuthenticationProvider() { + return nativeAuthenticationProvider; + } + + /** + * + * + * @return + */ + public String getNativeFileSystemProvider() { + return nativeFileSystemProvider; + } + + /** + * + * + * @return + */ + public String getNativeProcessProvider() { + return nativeProcessProvider; + } + + /** + * + * + * @param name + * + * @return + */ + public String getSetting(String name) { + return (String) nativeSettings.get(name); + } + + /** + * + * + * @param name + * @param defaultValue + * + * @return + */ + public String getSetting(String name, String defaultValue) { + if (nativeSettings.containsKey(name)) { + return (String) nativeSettings.get(name); + } else { + return defaultValue; + } + } + + /** + * + * + * @param name + * + * @return + */ + public boolean containsSetting(String name) { + return nativeSettings.containsKey(name); + } + + /** + * + * + * @return + */ + public VFSMount getVFSRoot() { + return vfsRoot; + } + + /** + * + * + * @return + */ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += ("<!-- Platform Configuration file, Determines the behaviour of platform specific services -->\n<" + + PLATFORM_ELEMENT + ">\n"); + xml += " <!-- The process provider for executing and redirecting a process -->\n"; + xml += (" <" + NATIVE_PROCESS_ELEMENT + ">" + nativeProcessProvider + + "</" + NATIVE_PROCESS_ELEMENT + ">\n"); + xml += " <!-- The authentication provider for authenticating users and obtaining user information -->\n"; + xml += (" <" + NATIVE_AUTH_ELEMENT + ">" + + nativeAuthenticationProvider + "</" + NATIVE_AUTH_ELEMENT + ">\n"); + xml += " <!-- Native settings which may be used by the process or authentication provider -->\n"; + + Map.Entry entry; + Iterator it = nativeSettings.entrySet().iterator(); + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + xml += (" " + "<" + NATIVE_SETTING_ELEMENT + " " + + NAME_ATTRIBUTE + "=\"" + entry.getKey().toString() + "\" " + + VALUE_ATTRIBUTE + "=\"" + entry.getValue().toString() + "\"/>\n"); + } + + if (vfsRoot != null) { + xml += (" " + "<" + VFSROOT_ELEMENT + " path=\"" + vfsRoot + + "\"/>\n"); + } + + it = vfsMounts.entrySet().iterator(); + + String path; + String mount; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + path = (String) entry.getValue(); + mount = (String) entry.getKey(); + xml += (" " + "<" + VFSMOUNT_ELEMENT + " " + + (mount.equals(path) ? "" + : (MOUNT_ATTRIBUTE + "=\"" + + entry.getKey().toString() + "\" ")) + PATH_ATTRIBUTE + "=\"" + + entry.getValue().toString() + "\"/>\n"); + } + + xml += ("</" + PLATFORM_ELEMENT + ">"); + + return xml; + } + + class PlatformConfigurationSAXHandler extends DefaultHandler { + private VFSMount currentMount = null; + + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (qname.equals(PLATFORM_ELEMENT)) { + currentElement = qname; + } + + nativeProcessProvider = null; + nativeAuthenticationProvider = null; + nativeSettings.clear(); + } else { + if (currentElement.equals(PLATFORM_ELEMENT)) { + if (!qname.equals(NATIVE_PROCESS_ELEMENT) && + !qname.equals(NATIVE_AUTH_ELEMENT) && + !qname.equals(NATIVE_SETTING_ELEMENT) && + !qname.equals(VFSMOUNT_ELEMENT) && + !qname.equals(VFSROOT_ELEMENT) && + !qname.equals(NFS_ELEMENT)) { + throw new SAXException("Unexpected element " + qname); + } + } else if (currentElement.equals(VFSMOUNT_ELEMENT)) { + if (!qname.equals(VFSPERMISSION_ELEMENT)) { + throw new SAXException("Unexpected element " + qname); + } + } else { + throw new SAXException("Unexpected element " + qname); + } + + currentElement = qname; + + if (qname.equals(NATIVE_SETTING_ELEMENT)) { + String name = attrs.getValue(NAME_ATTRIBUTE); + String value = attrs.getValue(VALUE_ATTRIBUTE); + + if ((name == null) || (value == null)) { + throw new SAXException( + "Required attributes missing for NativeSetting element"); + } + + log.debug("NativeSetting " + name + "=" + value); + nativeSettings.put(name, value); + } + + if (qname.equals(VFSPERMISSION_ELEMENT)) { + String name = attrs.getValue(NAME_ATTRIBUTE); + String permissions = attrs.getValue(PERMISSIONS_ATTRIBUTE); + currentMount.setPermissions(new VFSPermission(name, + permissions)); + } + + if (qname.equals(VFSMOUNT_ELEMENT)) { + String path = attrs.getValue(PATH_ATTRIBUTE); + String mount = attrs.getValue(MOUNT_ATTRIBUTE); + String permissions = attrs.getValue(PERMISSIONS_ATTRIBUTE); + + if ((path != null) && (mount != null)) { + // verify the mount - must start with / and be unique + if (!mount.trim().equals("/")) { + try { + currentMount = new VFSMount(mount, path); + + if (permissions == null) { + currentMount.setPermissions(new VFSPermission( + "default")); + } else { + currentMount.setPermissions(new VFSPermission( + "default", permissions)); + } + + if (!vfsMounts.containsKey( + currentMount.getMount())) { + vfsMounts.put(currentMount.getMount(), + currentMount); + } else { + throw new SAXException("The mount " + + mount + " is already defined"); + } + } catch (IOException ex1) { + throw new SAXException( + "VFSMount element is invalid mount=" + + mount + " path=" + path); + } + } else { + throw new SAXException( + "The root mount / cannot be configured, use <VFSRoot path=\"" + + path + "\"/> instead"); + } + } else { + throw new SAXException("Required " + PATH_ATTRIBUTE + + " attribute for element <" + VFSMOUNT_ELEMENT + + "> is missing"); + } + } + + if (qname.equals(VFSROOT_ELEMENT)) { + if (vfsRoot != null) { + throw new SAXException( + "Only one VFSRoot can be defined"); + } + + String path = attrs.getValue(PATH_ATTRIBUTE); + String permissions = attrs.getValue(PERMISSIONS_ATTRIBUTE); + + try { + vfsRoot = new VFSMount("/", path); + + if (permissions != null) { + vfsRoot.setPermissions(new VFSPermission( + "default", permissions)); + } else { + vfsRoot.setPermissions(new VFSPermission("default")); + } + + vfsRoot.setRoot(true); + } catch (IOException ex) { + throw new SAXException( + "VFSRoot element is invalid path=" + path); + } + } + } + } + + public void characters(char[] ch, int start, int length) + throws SAXException { + if (currentElement == null) { + throw new SAXException("Unexpected characters found"); + } + + if (currentElement.equals(NATIVE_AUTH_ELEMENT)) { + nativeAuthenticationProvider = new String(ch, start, length).trim(); + log.debug("NativeAuthenticationProvider=" + + nativeAuthenticationProvider); + + return; + } + + if (currentElement.equals(NATIVE_PROCESS_ELEMENT)) { + nativeProcessProvider = new String(ch, start, length).trim(); + log.debug("NativeProcessProvider=" + nativeProcessProvider); + + return; + } + + if (currentElement.equals(NFS_ELEMENT)) { + nativeFileSystemProvider = new String(ch, start, length).trim(); + log.debug("NativeFileSystemProvider=" + + nativeFileSystemProvider); + + return; + } + } + + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement == null) { + throw new SAXException("Unexpected end element for " + qname); + } + + if (!currentElement.equals(qname)) { + throw new SAXException("Unexpected end element found"); + } + + if (currentElement.equals(PLATFORM_ELEMENT)) { + currentElement = null; + + return; + } + + if (currentElement.equals(VFSPERMISSION_ELEMENT)) { + currentElement = VFSMOUNT_ELEMENT; + } else if (!currentElement.equals(NATIVE_SETTING_ELEMENT) && + !currentElement.equals(NATIVE_AUTH_ELEMENT) && + !currentElement.equals(NATIVE_PROCESS_ELEMENT) && + !currentElement.equals(NFS_ELEMENT) && + !currentElement.equals(VFSMOUNT_ELEMENT) && + !currentElement.equals(VFSROOT_ELEMENT)) { + throw new SAXException("Unexpected end element for " + qname); + } else { + currentElement = PLATFORM_ELEMENT; + } + } + } +} diff --git a/src/com/sshtools/daemon/configuration/ServerConfiguration.java b/src/com/sshtools/daemon/configuration/ServerConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..d5bb6925288ce3db94f9a3f5354759761de9a124 --- /dev/null +++ b/src/com/sshtools/daemon/configuration/ServerConfiguration.java @@ -0,0 +1,507 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.configuration; + +import com.sshtools.daemon.session.*; + +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.transport.publickey.*; + +import org.apache.commons.logging.*; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import java.io.*; + +import java.util.*; + +import javax.xml.parsers.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class ServerConfiguration extends DefaultHandler { + private static Log log = LogFactory.getLog(ServerConfiguration.class); + private Map allowedSubsystems = new HashMap(); + private Map serverHostKeys = new HashMap(); + private List allowedAuthentications = new ArrayList(); + private List requiredAuthentications = new ArrayList(); + private int commandPort = 12222; + private int port = 22; + private String listenAddress = "0.0.0.0"; + private int maxConnections = 10; + private int maxAuthentications = 5; + private String terminalProvider = ""; + private String authorizationFile = "authorization.xml"; + private String userConfigDirectory = "%D/.ssh2"; + private String authenticationBanner = ""; + private boolean allowTcpForwarding = true; + private String currentElement = null; + private Class sessionChannelImpl = SessionChannelServer.class; + + /** + * Creates a new ServerConfiguration object. + * + * @param in + * + * @throws SAXException + * @throws ParserConfigurationException + * @throws IOException + */ + public ServerConfiguration(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + reload(in); + } + + /** + * + * + * @param in + * + * @throws SAXException + * @throws ParserConfigurationException + * @throws IOException + */ + public void reload(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + allowedSubsystems.clear(); + serverHostKeys.clear(); + allowedAuthentications.clear(); + requiredAuthentications.clear(); + commandPort = 12222; + port = 22; + listenAddress = "0.0.0.0"; + maxConnections = 10; + maxAuthentications = 5; + terminalProvider = ""; + authorizationFile = "authorization.xml"; + userConfigDirectory = "%D/.ssh2"; + authenticationBanner = ""; + allowTcpForwarding = true; + currentElement = null; + + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + saxParser.parse(in, this); + } + + /** + * + * + * @param uri + * @param localName + * @param qname + * @param attrs + * + * @throws SAXException + */ + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (!qname.equals("ServerConfiguration")) { + throw new SAXException("Unexpected root element " + qname); + } + } else { + if (currentElement.equals("ServerConfiguration")) { + if (qname.equals("ServerHostKey")) { + //String algorithm = attrs.getValue("AlgorithmName"); + String privateKey = attrs.getValue("PrivateKeyFile"); + + if (privateKey == null) { + throw new SAXException( + "Required attributes missing from <ServerHostKey> element"); + } + + log.debug("ServerHostKey PrivateKeyFile=" + privateKey); + + File f = new File(privateKey); + + if (!f.exists()) { + privateKey = ConfigurationLoader.getConfigurationDirectory() + + privateKey; + f = new File(privateKey); + } + + try { + if (f.exists()) { + SshPrivateKeyFile pkf = SshPrivateKeyFile.parse(f); + SshPrivateKey key = pkf.toPrivateKey(null); + serverHostKeys.put(key.getAlgorithmName(), key); + } else { + log.warn("Private key file '" + privateKey + + "' could not be found"); + } + } catch (InvalidSshKeyException ex) { + log.warn("Failed to load private key '" + privateKey, ex); + } catch (IOException ioe) { + log.warn("Failed to load private key '" + privateKey, + ioe); + } + } else if (qname.equals("Subsystem")) { + String type = attrs.getValue("Type"); + String name = attrs.getValue("Name"); + String provider = attrs.getValue("Provider"); + + if ((type == null) || (name == null) || (provider == null)) { + throw new SAXException( + "Required attributes missing from <Subsystem> element"); + } + + log.debug("Subsystem Type=" + type + " Name=" + name + + " Provider=" + provider); + allowedSubsystems.put(name, + new AllowedSubsystem(type, name, provider)); + } else if (!qname.equals("AuthenticationBanner") && + !qname.equals("MaxConnections") && + !qname.equals("MaxAuthentications") && + !qname.equals("ListenAddress") && + !qname.equals("Port") && !qname.equals("CommandPort") && + !qname.equals("TerminalProvider") && + !qname.equals("AllowedAuthentication") && + !qname.equals("RequiredAuthentication") && + !qname.equals("AuthorizationFile") && + !qname.equals("UserConfigDirectory") && + !qname.equals("AllowTcpForwarding")) { + throw new SAXException("Unexpected <" + qname + + "> element after SshAPIConfiguration"); + } + } + } + + currentElement = qname; + } + + /** + * + * + * @param ch + * @param start + * @param length + * + * @throws SAXException + */ + public void characters(char[] ch, int start, int length) + throws SAXException { + String value = new String(ch, start, length); + + if (currentElement != null) { + if (currentElement.equals("AuthenticationBanner")) { + authenticationBanner = value; + log.debug("AuthenticationBanner=" + authenticationBanner); + } else if (currentElement.equals("MaxConnections")) { + maxConnections = Integer.parseInt(value); + log.debug("MaxConnections=" + value); + } else if (currentElement.equals("ListenAddress")) { + listenAddress = value; + log.debug("ListenAddress=" + listenAddress); + } else if (currentElement.equals("Port")) { + port = Integer.parseInt(value); + log.debug("Port=" + value); + } else if (currentElement.equals("CommandPort")) { + commandPort = Integer.parseInt(value); + log.debug("CommandPort=" + value); + } else if (currentElement.equals("TerminalProvider")) { + terminalProvider = value; + log.debug("TerminalProvider=" + terminalProvider); + } else if (currentElement.equals("AllowedAuthentication")) { + if (!allowedAuthentications.contains(value)) { + allowedAuthentications.add(value); + log.debug("AllowedAuthentication=" + value); + } + } else if (currentElement.equals("RequiredAuthentication")) { + if (!requiredAuthentications.contains(value)) { + requiredAuthentications.add(value); + log.debug("RequiredAuthentication=" + value); + } + } else if (currentElement.equals("AuthorizationFile")) { + authorizationFile = value; + log.debug("AuthorizationFile=" + authorizationFile); + } else if (currentElement.equals("UserConfigDirectory")) { + userConfigDirectory = value; + log.debug("UserConfigDirectory=" + userConfigDirectory); + } else if (currentElement.equals("SessionChannelImpl")) { + try { + sessionChannelImpl = ConfigurationLoader.getExtensionClass(value); + } catch (Exception e) { + log.error("Failed to load SessionChannelImpl " + value, e); + } + } else if (currentElement.equals("MaxAuthentications")) { + maxAuthentications = Integer.parseInt(value); + log.debug("MaxAuthentications=" + value); + } else if (currentElement.equals("AllowTcpForwarding")) { + allowTcpForwarding = Boolean.valueOf(value).booleanValue(); + } + } + } + + /** + * + * + * @param uri + * @param localName + * @param qname + * + * @throws SAXException + */ + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement != null) { + if (!currentElement.equals(qname)) { + throw new SAXException("Unexpected end element found <" + + qname + ">"); + } else if (currentElement.equals("ServerConfiguration")) { + currentElement = null; + } else if (currentElement.equals("AuthenticationBanner") || + currentElement.equals("ServerHostKey") || + currentElement.equals("Subsystem") || + currentElement.equals("MaxConnections") || + currentElement.equals("MaxAuthentications") || + currentElement.equals("ListenAddress") || + currentElement.equals("Port") || + currentElement.equals("CommandPort") || + currentElement.equals("TerminalProvider") || + currentElement.equals("AllowedAuthentication") || + currentElement.equals("RequiredAuthentication") || + currentElement.equals("AuthorizationFile") || + currentElement.equals("UserConfigDirectory") || + currentElement.equals("AllowTcpForwarding")) { + currentElement = "ServerConfiguration"; + } + } else { + throw new SAXException("Unexpected end element <" + qname + + "> found"); + } + } + + /** + * + * + * @return + */ + public List getRequiredAuthentications() { + return requiredAuthentications; + } + + /** + * + * + * @return + */ + public List getAllowedAuthentications() { + return allowedAuthentications; + } + + /** + * + * + * @return + */ + public boolean getAllowTcpForwarding() { + return allowTcpForwarding; + } + + /** + * + * + * @return + */ + public String getAuthenticationBanner() { + return authenticationBanner; + } + + /** + * + * + * @return + */ + public int getCommandPort() { + return commandPort; + } + + /** + * + * + * @return + */ + public String getUserConfigDirectory() { + return userConfigDirectory; + } + + /** + * + * + * @return + */ + public String getAuthorizationFile() { + return authorizationFile; + } + + /** + * + * + * @return + */ + public String getListenAddress() { + return listenAddress; + } + + /** + * + * + * @return + */ + public int getMaxConnections() { + return maxConnections; + } + + /** + * + * + * @return + */ + public int getMaxAuthentications() { + return maxAuthentications; + } + + /** + * + * + * @return + */ + public int getPort() { + return port; + } + + /*public Class getSessionChannelImpl() { + return sessionChannelImpl; + }*/ + public Map getServerHostKeys() { + return serverHostKeys; + } + + /** + * + * + * @return + */ + public Map getSubsystems() { + return allowedSubsystems; + } + + /** + * + * + * @return + */ + public String getTerminalProvider() { + return terminalProvider; + } + + /** + * + * + * @return + */ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += "<!-- Server configuration file - If filenames are not absolute they are assummed to be in the same directory as this configuration file. -->\n"; + xml += "<ServerConfiguration>\n"; + xml += " <!-- The available host keys for server authentication -->\n"; + + Map.Entry entry; + Iterator it = serverHostKeys.entrySet().iterator(); + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + xml += (" <ServerHostKey PrivateKeyFile=\"" + entry.getValue() + + "\"/>\n"); + } + + xml += " <!-- Add any number of subsystem elements here -->\n"; + + AllowedSubsystem subsystem; + it = allowedSubsystems.entrySet().iterator(); + + while (it.hasNext()) { + subsystem = (AllowedSubsystem) ((Map.Entry) it.next()).getValue(); + xml += (" <Subsystem Name=\"" + subsystem.getName() + + "\" Type=\"" + subsystem.getType() + "\" Provider=\"" + + subsystem.getProvider() + "\"/>\n"); + } + + xml += " <!-- Display the following authentication banner before authentication -->\n"; + xml += (" <AuthenticationBanner>" + authenticationBanner + + "</AuthenticationBanner>\n"); + xml += " <!-- The maximum number of connected sessions available -->\n"; + xml += (" <MaxConnections>" + String.valueOf(maxConnections) + + "</MaxConnections>\n"); + xml += " <!-- The maximum number of authentication attemtps for each connection -->\n"; + xml += (" <MaxAuthentications>" + String.valueOf(maxAuthentications) + + "</MaxAuthentications>\n"); + xml += " <!-- Bind to the following address to listen for connections -->\n"; + xml += (" <ListenAddress>" + listenAddress + "</ListenAddress>\n"); + xml += " <!-- The port to listen to -->\n"; + xml += (" <Port>" + String.valueOf(port) + "</Port>\n"); + xml += " <!-- Listen on the following port (on localhost) for server commands such as stop -->\n"; + xml += (" <CommandPort>" + String.valueOf(commandPort) + + "</CommandPort>\n"); + xml += " <!-- Specify the executable that provides the default shell -->\n"; + xml += (" <TerminalProvider>" + terminalProvider + + "</TerminalProvider>\n"); + xml += " <!-- Specify any number of allowed authentications -->\n"; + it = allowedAuthentications.iterator(); + + while (it.hasNext()) { + xml += (" <AllowedAuthentication>" + it.next().toString() + + "</AllowedAuthentication>\n"); + } + + xml += " <!-- Specify any number of required authentications -->\n"; + it = requiredAuthentications.iterator(); + + while (it.hasNext()) { + xml += (" <RequiredAuthentication>" + it.next().toString() + + "</RequiredAuthentication>\n"); + } + + xml += " <!-- The users authorizations file -->\n"; + xml += (" <AuthorizationFile>" + authorizationFile + + "</AuthorizationFile>\n"); + xml += " <!-- The users configuration directory where files such as AuthorizationFile are found. For users home directory specify %D For users name specify %U -->\n"; + xml += (" <UserConfigDirectory>" + userConfigDirectory + + "</UserConfigDirectory>\n"); + xml += ("<AllowTcpForwarding>" + String.valueOf(allowTcpForwarding) + + "</AllowTcpForwarding>\n"); + xml += "</ServerConfiguration>\n"; + + return xml; + } +} diff --git a/src/com/sshtools/daemon/configuration/SshAPIConfiguration.java b/src/com/sshtools/daemon/configuration/SshAPIConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..9f9c1808d795f67be9c1376265371a7df5d693e4 --- /dev/null +++ b/src/com/sshtools/daemon/configuration/SshAPIConfiguration.java @@ -0,0 +1,634 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.configuration; + +import com.sshtools.j2ssh.configuration.ExtensionAlgorithm; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.IOException; +import java.io.InputStream; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class SshAPIConfiguration extends DefaultHandler + implements com.sshtools.j2ssh.configuration.SshAPIConfiguration { + private String defaultCipher = null; + private String defaultMac = null; + private String defaultCompression = null; + private String defaultPublicKey = null; + private String defaultKeyExchange = null; + private List cipherExtensions = new ArrayList(); + private List macExtensions = new ArrayList(); + private List compressionExtensions = new ArrayList(); + private List pkExtensions = new ArrayList(); + private List kexExtensions = new ArrayList(); + private List authExtensions = new ArrayList(); + private List pkFormats = new ArrayList(); + private List prvFormats = new ArrayList(); + private String defaultPublicFormat = null; + private String defaultPrivateFormat = null; + private String currentElement = null; + private String parentElement = null; + private List currentList = null; + private ExtensionAlgorithm currentExt = null; + + /** + * Creates a new SshAPIConfiguration object. + * + * @param in + * + * @throws SAXException + * @throws ParserConfigurationException + * @throws IOException + */ + public SshAPIConfiguration(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + reload(in); + } + + /** + * + * + * @param in + * + * @throws SAXException + * @throws ParserConfigurationException + * @throws IOException + */ + public void reload(InputStream in) + throws SAXException, ParserConfigurationException, IOException { + defaultCipher = null; + defaultMac = null; + defaultCompression = null; + defaultKeyExchange = null; + defaultPublicKey = null; + defaultPublicFormat = null; + defaultPrivateFormat = null; + cipherExtensions.clear(); + macExtensions.clear(); + compressionExtensions.clear(); + pkExtensions.clear(); + kexExtensions.clear(); + authExtensions.clear(); + pkFormats.clear(); + prvFormats.clear(); + + SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxFactory.newSAXParser(); + saxParser.parse(in, this); + } + + /** + * + * + * @param ch + * @param start + * @param length + * + * @throws SAXException + */ + public void characters(char[] ch, int start, int length) + throws SAXException { + String value = new String(ch, start, length); + + if (currentElement != null) { + if (currentElement.equals("AlgorithmName")) { + if (currentExt != null) { + currentExt.setAlgorithmName(value); + } else { + throw new SAXException("Unexpected AlgorithmName element!"); + } + + return; + } + + if (currentElement.equals("ImplementationClass")) { + if (currentExt != null) { + currentExt.setImplementationClass(value); + } else { + throw new SAXException( + "Unexpected ImplementationClass element!"); + } + + return; + } + + if (currentElement.equals("DefaultAlgorithm")) { + if (parentElement.equals("CipherConfiguration")) { + defaultCipher = value; + } else if (parentElement.equals("MacConfiguration")) { + defaultMac = value; + } else if (parentElement.equals("CompressionConfiguration")) { + defaultCompression = value; + } else if (parentElement.equals("PublicKeyConfiguration")) { + defaultPublicKey = value; + } else if (parentElement.equals("KeyExchangeConfiguration")) { + defaultKeyExchange = value; + } else { + throw new SAXException( + "Unexpected parent elemenet for DefaultAlgorithm element"); + } + } + + if (currentElement.equals("DefaultPublicFormat")) { + defaultPublicFormat = value; + } + + if (currentElement.equals("DefaultPrivateFormat")) { + defaultPrivateFormat = value; + } + } + } + + /** + * + * + * @param uri + * @param localName + * @param qname + * + * @throws SAXException + */ + public void endElement(String uri, String localName, String qname) + throws SAXException { + if (currentElement != null) { + if (!currentElement.equals(qname)) { + throw new SAXException("Unexpected end element found " + qname); + } else if (currentElement.equals("SshAPIConfiguration")) { + currentElement = null; + } else if (currentElement.equals("CipherConfiguration") || + currentElement.equals("MacConfiguration") || + currentElement.equals("PublicKeyConfiguration") || + currentElement.equals("CompressionConfiguration") || + currentElement.equals("KeyExchangeConfiguration") || + currentElement.equals("AuthenticationConfiguration")) { + currentList = null; + currentElement = "SshAPIConfiguration"; + } else if (currentElement.equals("ExtensionAlgorithm")) { + if (currentExt == null) { + throw new SAXException( + "Critical error, null extension algortihm"); + } + + if ((currentExt.getAlgorithmName() == null) || + (currentExt.getImplementationClass() == null)) { + throw new SAXException( + "Unexpected end of ExtensionAlgorithm Element"); + } + + currentList.add(currentExt); + currentExt = null; + currentElement = parentElement; + } else if (currentElement.equals("DefaultAlgorithm") || + currentElement.equals("DefaultPublicFormat") || + currentElement.equals("DefaultPrivateFormat") || + currentElement.equals("PublicKeyFormat") || + currentElement.equals("PrivateKeyFormat")) { + currentElement = parentElement; + } else if (currentElement.equals("AlgorithmName")) { + currentElement = "ExtensionAlgorithm"; + } else if (currentElement.equals("ImplementationClass")) { + currentElement = "ExtensionAlgorithm"; + } else { + throw new SAXException("Unexpected end element " + qname); + } + } + } + + /** + * + * + * @param uri + * @param localName + * @param qname + * @param attrs + * + * @throws SAXException + */ + public void startElement(String uri, String localName, String qname, + Attributes attrs) throws SAXException { + if (currentElement == null) { + if (!qname.equals("SshAPIConfiguration")) { + throw new SAXException("Unexpected root element " + qname); + } + } else { + if (currentElement.equals("SshAPIConfiguration")) { + if (!qname.equals("CipherConfiguration") && + !qname.equals("MacConfiguration") && + !qname.equals("CompressionConfiguration") && + !qname.equals("PublicKeyConfiguration") && + !qname.equals("AuthenticationConfiguration") && + !qname.equals("KeyExchangeConfiguration")) { + throw new SAXException("Unexpected <" + qname + + "> element after SshAPIConfiguration"); + } + } else if (currentElement.equals("CipherConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = cipherExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after CipherConfiguration"); + } + } else if (currentElement.equals("MacConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = macExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after CipherConfiguration"); + } + } else if (currentElement.equals("CompressionConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = compressionExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after CompressionConfiguration"); + } + } else if (currentElement.equals("PublicKeyConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = pkExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else if (qname.equals("PublicKeyFormat")) { + String cls = attrs.getValue("ImplementationClass"); + + if (cls == null) { + throw new SAXException( + "<PublicKeyFormat> element requries the ImplementationClass attribute"); + } + + pkFormats.add(cls); + } else if (qname.equals("PrivateKeyFormat")) { + String cls = attrs.getValue("ImplementationClass"); + + if (cls == null) { + throw new SAXException( + "<PrivateKeyFormat> element requries the ImplementationClass attribute"); + } + + prvFormats.add(cls); + } else if (qname.equals("DefaultPublicFormat")) { + parentElement = currentElement; + } else if (qname.equals("DefaultPrivateFormat")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after PublicKeyConfiguration"); + } + } else if (currentElement.equals("AuthenticationConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = authExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after AuthenticationConfiguration"); + } + } else if (currentElement.equals("KeyExchangeConfiguration")) { + if (qname.equals("ExtensionAlgorithm")) { + currentList = kexExtensions; + parentElement = currentElement; + currentExt = new ExtensionAlgorithm(); + } else if (qname.equals("DefaultAlgorithm")) { + parentElement = currentElement; + } else { + throw new SAXException("Unexpected element <" + qname + + "> found after KeyExchangeConfiguration"); + } + } else if ((currentElement.equals("ExtensionAlgorithm") && + qname.equals("AlgorithmName")) || + (currentElement.equals("ExtensionAlgorithm") && + qname.equals("ImplementationClass"))) { + } else { + throw new SAXException("Unexpected element " + qname); + } + } + + currentElement = qname; + } + + /** + * + * + * @return + */ + public List getCompressionExtensions() { + return compressionExtensions; + } + + /** + * + * + * @return + */ + public List getCipherExtensions() { + return cipherExtensions; + } + + /** + * + * + * @return + */ + public List getMacExtensions() { + return macExtensions; + } + + /** + * + * + * @return + */ + public List getAuthenticationExtensions() { + return authExtensions; + } + + /** + * + * + * @return + */ + public List getPublicKeyExtensions() { + return pkExtensions; + } + + /** + * + * + * @return + */ + public List getKeyExchangeExtensions() { + return kexExtensions; + } + + /** + * + * + * @return + */ + public String getDefaultCipher() { + return defaultCipher; + } + + /** + * + * + * @return + */ + public String getDefaultMac() { + return defaultMac; + } + + /** + * + * + * @return + */ + public String getDefaultCompression() { + return defaultCompression; + } + + /** + * + * + * @return + */ + public String getDefaultPublicKey() { + return defaultPublicKey; + } + + /** + * + * + * @return + */ + public String getDefaultKeyExchange() { + return defaultKeyExchange; + } + + /** + * + * + * @return + */ + public String getDefaultPublicKeyFormat() { + return defaultPublicFormat; + } + + /** + * + * + * @return + */ + public String getDefaultPrivateKeyFormat() { + return defaultPrivateFormat; + } + + /** + * + * + * @return + */ + public List getPublicKeyFormats() { + return pkFormats; + } + + /** + * + * + * @return + */ + public List getPrivateKeyFormats() { + return prvFormats; + } + + /** + * + * + * @return + */ + public String toString() { + String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += "<!-- Sshtools J2SSH Configuration file -->\n"; + xml += "<SshAPIConfiguration>\n"; + xml += " <!-- The Cipher configuration, add or overide default cipher implementations -->\n"; + xml += " <CipherConfiguration>\n"; + + Iterator it = cipherExtensions.iterator(); + ExtensionAlgorithm ext; + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultCipher + + "</DefaultAlgorithm>\n"); + xml += " </CipherConfiguration>\n"; + xml += " <!-- The Mac configuration, add or overide default mac implementations -->\n"; + xml += " <MacConfiguration>\n"; + it = macExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultMac + + "</DefaultAlgorithm>\n"); + xml += " </MacConfiguration>\n"; + xml += " <!-- The Compression configuration, add or overide default compression implementations -->\n"; + xml += " <CompressionConfiguration>\n"; + it = compressionExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultCompression + + "</DefaultAlgorithm>\n"); + xml += " </CompressionConfiguration>\n"; + xml += " <!-- The Public Key configuration, add or overide default public key implementations -->\n"; + xml += " <PublicKeyConfiguration>\n"; + it = pkExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultPublicKey + + "</DefaultAlgorithm>\n"); + it = pkFormats.iterator(); + + String cls; + + while (it.hasNext()) { + cls = (String) it.next(); + xml += (" <PublicKeyFormat ImplementationClass=\"" + cls + + "\"/>\n"); + } + + it = prvFormats.iterator(); + + while (it.hasNext()) { + cls = (String) it.next(); + xml += (" <PrivateKeyFormat ImplementationClass=\"" + cls + + "\"/>\n"); + } + + xml += (" <DefaultPublicFormat>" + defaultPublicFormat + + "</DefaultPublicFormat>\n"); + xml += (" <DefaultPrivateFormat>" + defaultPrivateFormat + + "</DefaultPrivateFormat>\n"); + xml += " </PublicKeyConfiguration>\n"; + xml += " <!-- The Key Exchange configuration, add or overide default key exchange implementations -->\n"; + xml += " <KeyExchangeConfiguration>\n"; + it = kexExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += (" <DefaultAlgorithm>" + defaultKeyExchange + + "</DefaultAlgorithm>\n"); + xml += " </KeyExchangeConfiguration>\n"; + xml += " <!-- The Authentication configuration, add or overide default authentication implementations -->\n"; + xml += " <AuthenticationConfiguration>\n"; + it = authExtensions.iterator(); + + while (it.hasNext()) { + ext = (ExtensionAlgorithm) it.next(); + xml += " <ExtensionAlgorithm>\n"; + xml += (" <AlgorithmName>" + ext.getAlgorithmName() + + "</AlgorithmName>\n"); + xml += (" <ImplementationClass>" + + ext.getImplementationClass() + "</ImplementationClass>\n"); + xml += " </ExtensionAlgorithm>\n"; + } + + xml += " </AuthenticationConfiguration>\n"; + xml += "</SshAPIConfiguration>"; + + return xml; + } +} diff --git a/src/com/sshtools/daemon/configuration/XmlServerConfigurationContext.java b/src/com/sshtools/daemon/configuration/XmlServerConfigurationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..cbd5848a5cfc49f753acdd46316ae6b4c34bf4b2 --- /dev/null +++ b/src/com/sshtools/daemon/configuration/XmlServerConfigurationContext.java @@ -0,0 +1,139 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.configuration; + +import com.sshtools.j2ssh.configuration.ConfigurationContext; +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import java.util.HashMap; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class XmlServerConfigurationContext implements ConfigurationContext { + HashMap configurations = new HashMap(); + String serverResource = null; + String platformResource = null; + boolean failOnError = true; + + /** + * Creates a new XmlServerConfigurationContext object. + */ + public XmlServerConfigurationContext() { + } + + /** + * + * + * @param serverResource + */ + public void setServerConfigurationResource(String serverResource) { + this.serverResource = serverResource; + } + + /** + * + * + * @param platformResource + */ + public void setPlatformConfigurationResource(String platformResource) { + this.platformResource = platformResource; + } + + /** + * + * + * @param failOnError + */ + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } + + /** + * + * + * @throws ConfigurationException + */ + public void initialize() throws ConfigurationException { + if (serverResource != null) { + try { + ServerConfiguration y = new ServerConfiguration(ConfigurationLoader.loadFile( + serverResource)); + configurations.put(ServerConfiguration.class, y); + } catch (Exception ex) { + if (failOnError) { + throw new ConfigurationException(ex.getMessage()); + } + } + } + + if (platformResource != null) { + try { + PlatformConfiguration z = new PlatformConfiguration(ConfigurationLoader.loadFile( + platformResource)); + configurations.put(PlatformConfiguration.class, z); + } catch (Exception ex) { + if (failOnError) { + throw new ConfigurationException(ex.getMessage()); + } + } + } + } + + /** + * + * + * @param cls + * + * @return + */ + public boolean isConfigurationAvailable(Class cls) { + return configurations.containsKey(cls); + } + + /** + * + * + * @param cls + * + * @return + * + * @throws ConfigurationException + */ + public Object getConfiguration(Class cls) throws ConfigurationException { + if (configurations.containsKey(cls)) { + return configurations.get(cls); + } else { + throw new ConfigurationException(cls.getName() + + " configuration not available"); + } + } +} diff --git a/src/com/sshtools/daemon/forwarding/ForwardingServer.java b/src/com/sshtools/daemon/forwarding/ForwardingServer.java new file mode 100644 index 0000000000000000000000000000000000000000..f90a2477b9413b54276496bfab2203e36222b2ef --- /dev/null +++ b/src/com/sshtools/daemon/forwarding/ForwardingServer.java @@ -0,0 +1,329 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.forwarding; + +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelFactory; +import com.sshtools.j2ssh.connection.ConnectionProtocol; +import com.sshtools.j2ssh.connection.GlobalRequestHandler; +import com.sshtools.j2ssh.connection.GlobalRequestResponse; +import com.sshtools.j2ssh.connection.InvalidChannelException; +import com.sshtools.j2ssh.forwarding.ForwardingClient; +import com.sshtools.j2ssh.forwarding.ForwardingConfiguration; +import com.sshtools.j2ssh.forwarding.ForwardingConfigurationException; +import com.sshtools.j2ssh.forwarding.ForwardingListener; +import com.sshtools.j2ssh.forwarding.ForwardingSocketChannel; +import com.sshtools.j2ssh.io.ByteArrayReader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.net.Socket; +import java.net.SocketPermission; + +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class ForwardingServer implements ChannelFactory, GlobalRequestHandler { + private static Log log = LogFactory.getLog(ForwardingServer.class); + private ConnectionProtocol connection; + private List channelTypes = new Vector(); + private List localForwardings = new Vector(); + private List remoteForwardings = new Vector(); + + /** + * Creates a new ForwardingServer object. + * + * @param connection + * + * @throws IOException + */ + public ForwardingServer(ConnectionProtocol connection) + throws IOException { + this.connection = connection; + channelTypes.add(ForwardingSocketChannel.LOCAL_FORWARDING_CHANNEL); + connection.addChannelFactory(ForwardingSocketChannel.LOCAL_FORWARDING_CHANNEL, + this); + connection.allowGlobalRequest(ForwardingClient.REMOTE_FORWARD_REQUEST, + this); + connection.allowGlobalRequest(ForwardingClient.REMOTE_FORWARD_CANCEL_REQUEST, + this); + } + + /* public List getChannelType() { + return channelTypes; + }*/ + public Channel createChannel(String channelType, byte[] requestData) + throws InvalidChannelException { + if (!channelType.equals( + ForwardingSocketChannel.LOCAL_FORWARDING_CHANNEL)) { + throw new InvalidChannelException( + "The client can only request the " + + "opening of a local forwarding channel"); + } + + try { + ByteArrayReader bar = new ByteArrayReader(requestData); + String hostToConnect = bar.readString(); + int portToConnect = (int) bar.readInt(); + String originatingHost = bar.readString(); + int originatingPort = (int) bar.readInt(); + + // Get a configuration item for the forwarding + ForwardingConfiguration config = getLocalForwardingByAddress(originatingHost, + originatingPort); + Socket socket = new Socket(hostToConnect, portToConnect); + + // Create the channel adding it to the active channels + ForwardingSocketChannel channel = config.createForwardingSocketChannel(channelType, + hostToConnect, portToConnect, originatingHost, + originatingPort); + channel.bindSocket(socket); + + return channel; + } catch (ForwardingConfigurationException fce) { + throw new InvalidChannelException( + "No valid forwarding configuration was available for the request"); + } catch (IOException ioe) { + throw new InvalidChannelException("The channel request data is " + + "invalid/or corrupt for channel type " + channelType); + } + } + + /** + * + * + * @param requestName + * @param requestData + * + * @return + */ + public GlobalRequestResponse processGlobalRequest(String requestName, + byte[] requestData) { + GlobalRequestResponse response = new GlobalRequestResponse(false); + String addressToBind = null; + int portToBind = -1; + log.debug("Processing " + requestName + " global request"); + + try { + ByteArrayReader bar = new ByteArrayReader(requestData); + addressToBind = bar.readString(); + portToBind = (int) bar.readInt(); + + if (requestName.equals(ForwardingClient.REMOTE_FORWARD_REQUEST)) { + addRemoteForwardingConfiguration(addressToBind, portToBind); + response = new GlobalRequestResponse(true); + } + + if (requestName.equals( + ForwardingClient.REMOTE_FORWARD_CANCEL_REQUEST)) { + removeRemoteForwarding(addressToBind, portToBind); + response = new GlobalRequestResponse(true); + } + } catch (IOException ioe) { + log.warn("The client failed to request " + requestName + " for " + + addressToBind + ":" + String.valueOf(portToBind), ioe); + } + + return response; + } + + /** + * + * + * @param orginatingAddress + * @param originatingPort + * + * @return + * + * @throws ForwardingConfigurationException + */ + protected ForwardingConfiguration getLocalForwardingByAddress( + String orginatingAddress, int originatingPort) + throws ForwardingConfigurationException { + try { + Iterator it = localForwardings.iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(orginatingAddress) && + (config.getPortToBind() == originatingPort)) { + return config; + } + } + + // No configuration is available so create one + config = new ForwardingConfiguration(orginatingAddress, + originatingPort); + + // We must start this configuration in order to use it + config.start(); + localForwardings.add(config); + + return config; + } catch (IOException ex) { + throw new ForwardingConfigurationException(ex.getMessage()); + } + } + + /** + * + * + * @param addressToBind + * @param portToBind + * + * @return + * + * @throws ForwardingConfigurationException + */ + protected ForwardingConfiguration getRemoteForwardingByAddress( + String addressToBind, int portToBind) + throws ForwardingConfigurationException { + Iterator it = remoteForwardings.iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(addressToBind) && + (config.getPortToBind() == portToBind)) { + return config; + } + } + + throw new ForwardingConfigurationException( + "The remote forwarding does not exist!"); + } + + /** + * + * + * @param addressToBind + * @param portToBind + * + * @throws ForwardingConfigurationException + */ + protected void addRemoteForwardingConfiguration(String addressToBind, + int portToBind) throws ForwardingConfigurationException { + // Is the server already listening + Iterator it = remoteForwardings.iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(addressToBind) && + (config.getPortToBind() == portToBind)) { + throw new ForwardingConfigurationException( + "The address and port are already in use!"); + } + } + + config = new ForwardingConfiguration(addressToBind, portToBind); + + // Check the security mananger + SecurityManager manager = System.getSecurityManager(); + + if (manager != null) { + try { + manager.checkPermission(new SocketPermission(addressToBind + + ":" + String.valueOf(portToBind), "accept,listen")); + } catch (SecurityException e) { + throw new ForwardingConfigurationException( + "The security manager has denied listen permision on " + + addressToBind + ":" + String.valueOf(portToBind)); + } + } + + try { + ForwardingListener listener = new ServerForwardingListener(connection, + addressToBind, portToBind); + remoteForwardings.add(listener); + listener.start(); + } catch (IOException ex) { + throw new ForwardingConfigurationException(ex.getMessage()); + } + } + + /** + * + * + * @param addressToBind + * @param portToBind + * + * @throws ForwardingConfigurationException + */ + protected void removeRemoteForwarding(String addressToBind, int portToBind) + throws ForwardingConfigurationException { + ForwardingConfiguration config = getRemoteForwardingByAddress(addressToBind, + portToBind); + + // Stop the forwarding + config.stop(); + + // Remove from the remote forwardings list + remoteForwardings.remove(config); + } + + class ServerForwardingListener extends ForwardingListener { + public ServerForwardingListener(ConnectionProtocol connection, + String addressToBind, int portToBind) { + super(connection, addressToBind, portToBind); + } + + public ForwardingSocketChannel createChannel(String hostToConnect, + int portToConnect, Socket socket) + throws ForwardingConfigurationException { + try { + ForwardingSocketChannel channel = createForwardingSocketChannel(ForwardingSocketChannel.REMOTE_FORWARDING_CHANNEL, + hostToConnect, portToConnect, + + /*( (InetSocketAddress) socket.getRemoteSocketAddress()).getAddress() + .getHostAddress(), + ( (InetSocketAddress) socket.getRemoteSocketAddress()) + .getPort()*/ + socket.getInetAddress().getHostAddress(), socket.getPort()); + channel.bindSocket(socket); + + return channel; + } catch (IOException ex) { + throw new ForwardingConfigurationException(ex.getMessage()); + } + } + } +} diff --git a/src/com/sshtools/daemon/platform/InvalidHandleException.java b/src/com/sshtools/daemon/platform/InvalidHandleException.java new file mode 100644 index 0000000000000000000000000000000000000000..f18f19db701e82c453e8d3b8b6acaa06a79e58db --- /dev/null +++ b/src/com/sshtools/daemon/platform/InvalidHandleException.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.platform; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class InvalidHandleException extends Exception { + /** + * Creates a new InvalidHandleException object. + * + * @param msg + */ + public InvalidHandleException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/daemon/platform/NativeAuthenticationProvider.java b/src/com/sshtools/daemon/platform/NativeAuthenticationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..83ea6da13db2322a5ebe93a4ee72f252f08f8971 --- /dev/null +++ b/src/com/sshtools/daemon/platform/NativeAuthenticationProvider.java @@ -0,0 +1,155 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.platform; + +import com.sshtools.daemon.configuration.*; + +import com.sshtools.j2ssh.configuration.*; + +import org.apache.commons.logging.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public abstract class NativeAuthenticationProvider { + private static Log log = LogFactory.getLog(NativeAuthenticationProvider.class); + private static Class cls; + private static NativeAuthenticationProvider instance; + + static { + try { + if (ConfigurationLoader.isConfigurationAvailable( + PlatformConfiguration.class)) { + cls = ConfigurationLoader.getExtensionClass(((PlatformConfiguration) ConfigurationLoader.getConfiguration( + PlatformConfiguration.class)).getNativeAuthenticationProvider()); + + // + } + } catch (Exception e) { + log.error("Failed to load native authentication provider", e); + instance = null; + } + } + + /** + * + * + * @param cls + */ + public static void setProvider(Class cls) { + NativeAuthenticationProvider.cls = cls; + } + + /** + * + * + * @param username + * + * @return + * + * @throws IOException + */ + public abstract String getHomeDirectory(String username) + throws IOException; + + /** + * + * + * @param username + * @param password + * + * @return + * + * @throws PasswordChangeException + * @throws IOException + */ + public abstract boolean logonUser(String username, String password) + throws PasswordChangeException, IOException; + + /** + * + * + * @param username + * + * @return + * + * @throws IOException + */ + public abstract boolean logonUser(String username) + throws IOException; + + /** + * + * + * @throws IOException + */ + public abstract void logoffUser() throws IOException; + + /** + * + * + * @param username + * @param oldpassword + * @param newpassword + * + * @return + */ + public abstract boolean changePassword(String username, String oldpassword, + String newpassword); + + /** + * + * + * @return + * + * @throws IOException + */ + public static NativeAuthenticationProvider getInstance() + throws IOException { + if (instance == null) { + try { + if (cls == null) { + throw new IOException( + "There is no authentication provider configured"); + } + + instance = (NativeAuthenticationProvider) cls.newInstance(); + } catch (Exception e) { + throw new IOException( + "The authentication provider failed to initialize: " + + e.getMessage()); + } + } + + return instance; + } +} diff --git a/src/com/sshtools/daemon/platform/NativeFileSystemProvider.java b/src/com/sshtools/daemon/platform/NativeFileSystemProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..7ff06b024aafe388f9646f949de18494dbc29b7d --- /dev/null +++ b/src/com/sshtools/daemon/platform/NativeFileSystemProvider.java @@ -0,0 +1,368 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.platform; + +import com.sshtools.daemon.configuration.*; + +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.sftp.*; + +import org.apache.commons.logging.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public abstract class NativeFileSystemProvider { + private static Log log = LogFactory.getLog(NativeAuthenticationProvider.class); + private static NativeFileSystemProvider instance; + + /** */ + public static final int OPEN_READ = SftpSubsystemClient.OPEN_READ; + + /** */ + public static final int OPEN_WRITE = SftpSubsystemClient.OPEN_WRITE; + + /** */ + public static final int OPEN_APPEND = SftpSubsystemClient.OPEN_APPEND; + + /** */ + public static final int OPEN_CREATE = SftpSubsystemClient.OPEN_CREATE; + + /** */ + public static final int OPEN_TRUNCATE = SftpSubsystemClient.OPEN_TRUNCATE; + + /** */ + public static final int OPEN_EXCLUSIVE = SftpSubsystemClient.OPEN_EXCLUSIVE; + + static { + try { + if (ConfigurationLoader.isConfigurationAvailable( + PlatformConfiguration.class)) { + Class cls = ConfigurationLoader.getExtensionClass(((PlatformConfiguration) ConfigurationLoader.getConfiguration( + PlatformConfiguration.class)).getNativeFileSystemProvider()); + instance = (NativeFileSystemProvider) cls.newInstance(); + } + } catch (Exception e) { + log.error("Failed to load native file system provider", e); + instance = null; + } + } + + /** + * + * + * @param path + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract boolean fileExists(String path) throws IOException; + + /** + * + * + * @param path + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract String getCanonicalPath(String path) + throws IOException, FileNotFoundException; + + /** + * + * + * @param path + * + * @return + * + * @throws FileNotFoundException + */ + public abstract String getRealPath(String path) + throws FileNotFoundException; + + /** + * + * + * @param path + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract boolean makeDirectory(String path) + throws PermissionDeniedException, FileNotFoundException, IOException; + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + * @throws FileNotFoundException + */ + public abstract FileAttributes getFileAttributes(String path) + throws IOException, FileNotFoundException; + + /** + * + * + * @param handle + * + * @return + * + * @throws IOException + * @throws InvalidHandleException + */ + public abstract FileAttributes getFileAttributes(byte[] handle) + throws IOException, InvalidHandleException; + + /** + * + * + * @param path + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract byte[] openDirectory(String path) + throws PermissionDeniedException, FileNotFoundException, IOException; + + /** + * + * + * @param handle + * + * @return + * + * @throws InvalidHandleException + * @throws EOFException + */ + public abstract SftpFile[] readDirectory(byte[] handle) + throws InvalidHandleException, EOFException, IOException; + + /** + * + * + * @param path + * @param flags + * @param attrs + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract byte[] openFile(String path, UnsignedInteger32 flags, + FileAttributes attrs) + throws PermissionDeniedException, FileNotFoundException, IOException; + + /** + * + * + * @param handle + * @param offset + * @param len + * + * @return + * + * @throws InvalidHandleException + * @throws EOFException + * @throws IOException + */ + public abstract byte[] readFile(byte[] handle, UnsignedInteger64 offset, + UnsignedInteger32 len) + throws InvalidHandleException, EOFException, IOException; + + /** + * + * + * @param handle + * @param offset + * @param data + * @param off + * @param len + * + * @throws InvalidHandleException + * @throws IOException + */ + public abstract void writeFile(byte[] handle, UnsignedInteger64 offset, + byte[] data, int off, int len) + throws InvalidHandleException, IOException; + + /** + * + * + * @param handle + * + * @throws InvalidHandleException + * @throws IOException + */ + public abstract void closeFile(byte[] handle) + throws InvalidHandleException, IOException; + + /** + * + * + * @param path + * + * @throws PermissionDeniedException + * @throws IOException + * @throws FileNotFoundException + */ + public abstract void removeFile(String path) + throws PermissionDeniedException, IOException, FileNotFoundException; + + /** + * + * + * @param oldpath + * @param newpath + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract void renameFile(String oldpath, String newpath) + throws PermissionDeniedException, FileNotFoundException, IOException; + + /** + * + * + * @param path + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract void removeDirectory(String path) + throws PermissionDeniedException, FileNotFoundException, IOException; + + /** + * + * + * @param path + * @param attrs + * + * @throws PermissionDeniedException + * @throws IOException + * @throws FileNotFoundException + */ + public abstract void setFileAttributes(String path, FileAttributes attrs) + throws PermissionDeniedException, IOException, FileNotFoundException; + + /** + * + * + * @param handle + * @param attrs + * + * @throws PermissionDeniedException + * @throws IOException + * @throws InvalidHandleException + */ + public abstract void setFileAttributes(byte[] handle, FileAttributes attrs) + throws PermissionDeniedException, IOException, InvalidHandleException; + + /** + * + * + * @param path + * + * @return + * + * @throws UnsupportedFileOperationException + * @throws FileNotFoundException + * @throws IOException + * @throws PermissionDeniedException + */ + public abstract SftpFile readSymbolicLink(String path) + throws UnsupportedFileOperationException, FileNotFoundException, + IOException, PermissionDeniedException; + + /** + * + * + * @param link + * @param target + * + * @throws UnsupportedFileOperationException + * @throws FileNotFoundException + * @throws IOException + * @throws PermissionDeniedException + */ + public abstract void createSymbolicLink(String link, String target) + throws UnsupportedFileOperationException, FileNotFoundException, + IOException, PermissionDeniedException; + + public abstract String getDefaultPath(String username) + throws FileNotFoundException; + + /** + * + * + * @param username + * @param path + * @param permissions + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public abstract void verifyPermissions(String username, String path, + String permissions) + throws PermissionDeniedException, FileNotFoundException, IOException; + + /** + * + * + * @return + */ + public static NativeFileSystemProvider getInstance() { + return instance; + } +} diff --git a/src/com/sshtools/daemon/platform/NativeProcessProvider.java b/src/com/sshtools/daemon/platform/NativeProcessProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..638fb9624ce59d26613f78d356b3660e4cfa3f76 --- /dev/null +++ b/src/com/sshtools/daemon/platform/NativeProcessProvider.java @@ -0,0 +1,187 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.platform; + +import com.sshtools.daemon.configuration.PlatformConfiguration; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public abstract class NativeProcessProvider { //implements SessionDataProvider { + + private static Log log = LogFactory.getLog(NativeProcessProvider.class); + private static Class provider; + + static { + try { + if (ConfigurationLoader.isConfigurationAvailable( + PlatformConfiguration.class)) { + provider = ConfigurationLoader.getExtensionClass(((PlatformConfiguration) ConfigurationLoader.getConfiguration( + PlatformConfiguration.class)).getNativeProcessProvider()); + } + } catch (Exception e) { + log.error("Failed to load native process provider", e); + provider = null; + } + } + + /** + * + * + * @return + * + * @throws IOException + */ + public static NativeProcessProvider newInstance() throws IOException { + try { + return (NativeProcessProvider) provider.newInstance(); + } catch (Exception e) { + throw new IOException( + "The process provider failed to create a new instance: " + + e.getMessage()); + } + } + + /** + * + * + * @param provider + */ + public static void setProvider(Class provider) { + NativeProcessProvider.provider = provider; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public abstract InputStream getInputStream() throws IOException; + + /** + * + * + * @return + * + * @throws IOException + */ + public abstract OutputStream getOutputStream() throws IOException; + + /** + * + * + * @return + */ + public abstract InputStream getStderrInputStream() + throws IOException; + + /** + * + */ + public abstract void kill(); + + /** + * + * + * @return + */ + public abstract boolean stillActive(); + + /** + * + * + * @return + */ + public abstract int waitForExitCode(); + + /** + * + * + * @return + */ + public abstract String getDefaultTerminalProvider(); + + /** + * + * + * @param command + * @param environment + * + * @return + * + * @throws IOException + */ + public abstract boolean createProcess(String command, Map environment) + throws IOException; + + /** + * + * + * @throws IOException + */ + public abstract void start() throws IOException; + + /** + * + * + * @param term + * + * @return + */ + public abstract boolean supportsPseudoTerminal(String term); + + /** + * + * + * @param term + * @param cols + * @param rows + * @param width + * @param height + * @param modes + * + * @return + */ + public abstract boolean allocatePseudoTerminal(String term, int cols, + int rows, int width, int height, String modes); +} diff --git a/src/com/sshtools/daemon/platform/PasswordChangeException.java b/src/com/sshtools/daemon/platform/PasswordChangeException.java new file mode 100644 index 0000000000000000000000000000000000000000..57a9e7373ea7ca997806f953dea90b67debf2473 --- /dev/null +++ b/src/com/sshtools/daemon/platform/PasswordChangeException.java @@ -0,0 +1,41 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.platform; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class PasswordChangeException extends Exception { + /** + * Creates a new PasswordChangeException object. + */ + public PasswordChangeException() { + } +} diff --git a/src/com/sshtools/daemon/platform/PermissionDeniedException.java b/src/com/sshtools/daemon/platform/PermissionDeniedException.java new file mode 100644 index 0000000000000000000000000000000000000000..12748c2395fec11f981140b5d651f11307c28d09 --- /dev/null +++ b/src/com/sshtools/daemon/platform/PermissionDeniedException.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.platform; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class PermissionDeniedException extends Exception { + /** + * Creates a new PermissionDeniedException object. + * + * @param msg + */ + public PermissionDeniedException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/daemon/platform/UnsupportedFileOperationException.java b/src/com/sshtools/daemon/platform/UnsupportedFileOperationException.java new file mode 100644 index 0000000000000000000000000000000000000000..02b1af36a9498bfd50978c0bc1eae34fd0f2be4d --- /dev/null +++ b/src/com/sshtools/daemon/platform/UnsupportedFileOperationException.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.platform; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class UnsupportedFileOperationException extends Exception { + /** + * Creates a new UnsupportedFileOperationException object. + * + * @param msg + */ + public UnsupportedFileOperationException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/daemon/scp/ScpServer.java b/src/com/sshtools/daemon/scp/ScpServer.java new file mode 100644 index 0000000000000000000000000000000000000000..6e7fb5db5e09c1436da78146e370138939ab865e --- /dev/null +++ b/src/com/sshtools/daemon/scp/ScpServer.java @@ -0,0 +1,837 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.scp; + +import com.sshtools.daemon.platform.*; +import com.sshtools.daemon.util.*; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.sftp.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.8 $ + */ +public class ScpServer extends NativeProcessProvider implements Runnable { + private static Log log = LogFactory.getLog(ScpServer.class); + private static int BUFFER_SIZE = 16384; + + // Private instance variables + private InputStream in; + + // Private instance variables + private InputStream err; + private OutputStream out; + private String destination; + private PipedOutputStream pipeIn; + private PipedOutputStream pipeErr; + private PipedInputStream pipeOut; + private SshThread scpServerThread; + private int verbosity = 0; + private int exitCode; + private boolean directory; + private boolean recursive; + private boolean from; + private boolean to; + private NativeFileSystemProvider nfs; + private byte[] buffer = new byte[BUFFER_SIZE]; + private String currentDirectory; + private boolean preserveAttributes; + + /** + * Creates a new ScpServer object. + */ + public ScpServer() { + nfs = NativeFileSystemProvider.getInstance(); + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#allocatePseudoTerminal(java.lang.String, int, int, int, int, java.lang.String) + */ + public boolean allocatePseudoTerminal(String term, int cols, int rows, + int width, int height, String modes) { + return false; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#createProcess(java.lang.String, java.util.Map) + */ + public boolean createProcess(String command, Map environment) + throws IOException { + log.info("Creating ScpServer"); + + if (nfs == null) { + throw new IOException( + "NativeFileSystem was not instantiated. Please check logs"); + } + + scp(command.substring(4)); + + return true; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#getDefaultTerminalProvider() + */ + public String getDefaultTerminalProvider() { + return null; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#getInputStream() + */ + public InputStream getInputStream() throws IOException { + return in; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#getStderrInputStream() + */ + public InputStream getStderrInputStream() { + return err; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#getOutputStream() + */ + public OutputStream getOutputStream() throws IOException { + return out; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#kill() + */ + public void kill() { + log.info("Killing ScpServer"); + + try { + if (pipeIn != null) { + pipeIn.close(); + } + } catch (IOException ioe) { + } + + try { + if (pipeOut != null) { + pipeOut.close(); + } + } catch (IOException ioe) { + } + + try { + if (pipeErr != null) { + pipeErr.close(); + } + } catch (IOException ioe) { + } + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#start() + */ + public void start() throws IOException { + log.debug("Starting ScpServer thread"); + scpServerThread = SshThread.getCurrentThread().cloneThread(this, + "ScpServer"); + scpServerThread.start(); + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#stillActive() + */ + public boolean stillActive() { + return false; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#supportsPseudoTerminal(java.lang.String) + */ + public boolean supportsPseudoTerminal(String term) { + return false; + } + + /* (non-Javadoc) + * @see com.sshtools.daemon.platform.NativeProcessProvider#waitForExitCode() + */ + public int waitForExitCode() { + try { + synchronized (this) { + wait(); + } + } catch (InterruptedException ie) { + } + + log.debug("Returning exit code of " + exitCode); + + return exitCode; + } + + private void scp(String args) throws IOException { + log.debug("Parsing ScpServer options " + args); + + // Parse the command line for supported options + String[] a = StringUtil.current().allParts(args, " "); + destination = null; + directory = false; + from = false; + to = false; + recursive = false; + verbosity = 0; + + boolean remote = false; + + for (int i = 0; i < a.length; i++) { + if (a[i].startsWith("-")) { + String s = a[i].substring(1); + + for (int j = 0; j < s.length(); j++) { + char ch = s.charAt(j); + + switch (ch) { + case 't': + to = true; + + break; + + case 'd': + directory = true; + + break; + + case 'f': + from = true; + + break; + + case 'r': + recursive = true; + + break; + + case 'v': + verbosity++; + + break; + + case 'p': + preserveAttributes = true; + + break; + + default: + log.warn("Unsupported argument, allowing to continue."); + } + } + } else { + if (destination == null) { + destination = a[i]; + } else { + throw new IOException("More than one destination supplied " + + a[i]); + } + } + } + + if (!to && !from) { + throw new IOException("Must supply either -t or -f."); + } + + if (destination == null) { + throw new IOException("Destination not supplied."); + } + + log.debug("Destination is " + destination); + log.debug("Recursive is " + recursive); + log.debug("Directory is " + directory); + log.debug("Verbosity is " + verbosity); + log.debug("From is " + from); + log.debug("To is " + to); + log.debug("Preserve Attributes " + preserveAttributes); + + // Start the SCP server + log.debug("Creating pipes"); + pipeIn = new PipedOutputStream(); + pipeErr = new PipedOutputStream(); + pipeOut = new PipedInputStream(); + in = new PipedInputStream(pipeIn); + err = new PipedInputStream(pipeErr); + out = new PipedOutputStream(pipeOut); + } + + /** + * Send ok command to client + * + * @throws IOException on any error + */ + private void writeOk() throws IOException { + log.debug("Sending client ok command"); + pipeIn.write(0); + pipeIn.flush(); + } + + /** + * Send command to client + * + * @param cmd command + * + * @throws IOException on any error + */ + private void writeCommand(String cmd) throws IOException { + log.debug("Sending command '" + cmd + "'"); + pipeIn.write(cmd.getBytes()); + + if (!cmd.endsWith("\n")) { + pipeIn.write("\n".getBytes()); + } + + pipeIn.flush(); + } + + /** + * Send error message to client + * + * @param msg error message + * + * @throws IOException on any error + */ + private void writeError(String msg) throws IOException { + writeError(msg, false); + } + + /** + * Send error message to client + * + * @param msg error message + * @param serious serious error + * + * @throws IOException on any error + */ + private void writeError(String msg, boolean serious) + throws IOException { + log.debug("Sending error message '" + msg + "' to client (serious=" + + serious + ")"); + pipeIn.write(serious ? 2 : 1); + pipeIn.write(msg.getBytes()); + + if (!msg.endsWith("\n")) { + pipeIn.write('\n'); + } + + pipeIn.flush(); + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + public void run() { + log.debug("Running ScpServer thread"); + + try { + if (from) { + log.info("From mode"); + + try { + waitForResponse(); + + // Build a string pattern that may be used to match wildcards + StringPattern sp = new StringPattern(destination); + + /*If this looks like a wildcard, then attempt a simple expansion. + * This only work for the base part of the file name at the moment + */ + if (sp.hasWildcard()) { + log.debug("Path contains wildcard"); + + String base = destination; + String dir = "."; + int idx = base.lastIndexOf('/'); + + if (idx != -1) { + if (idx > 0) { + dir = base.substring(0, idx); + } + + base = base.substring(idx + 1); + } + + log.debug("Looking for matches in " + dir + " for " + + base); + sp = new StringPattern(base); + + byte[] handle = null; + + try { + handle = nfs.openDirectory(dir); + + SftpFile[] files = nfs.readDirectory(handle); + + for (int i = 0; i < files.length; i++) { + log.debug("Testing for match against " + + files[i].getFilename()); + + if (sp.matches(files[i].getFilename())) { + log.debug("Matched"); + writeFileToRemote(dir + "/" + + files[i].getFilename()); + } else { + log.debug("No match"); + } + } + } finally { + if (handle != null) { + try { + nfs.closeFile(handle); + } catch (Exception e) { + } + } + } + } else { + log.debug("No wildcards"); + writeFileToRemote(destination); + } + + log.debug("File transfers complete"); + } catch (FileNotFoundException fnfe) { + log.error(fnfe); + writeError(fnfe.getMessage(), true); + throw new IOException(fnfe.getMessage()); + } catch (PermissionDeniedException pde) { + log.error(pde); + writeError(pde.getMessage(), true); + throw new IOException(pde.getMessage()); + } catch (InvalidHandleException ihe) { + log.error(ihe); + writeError(ihe.getMessage(), true); + throw new IOException(ihe.getMessage()); + } catch (IOException ioe) { + log.error(ioe); + writeError(ioe.getMessage(), true); + throw new IOException(ioe.getMessage()); + } + } else { + log.info("To mode"); + readFromRemote(destination); + } + } catch (Throwable t) { + t.printStackTrace(); + log.error(t); + exitCode = 1; + } + + // + log.debug("ScpServer stopped, notify block on waitForExitCode()."); + + synchronized (this) { + notify(); + } + } + + private boolean writeDirToRemote(String path) throws IOException { + FileAttributes attr = nfs.getFileAttributes(path); + + if (attr.isDirectory() && !recursive) { + writeError("File " + path + " is a directory, use recursive mode"); + + return false; + } + + String basename = path; + int idx = path.lastIndexOf('/'); + + if (idx != -1) { + basename = path.substring(idx + 1); + } + + writeCommand("D" + attr.getMaskString() + " 0 " + basename + "\n"); + waitForResponse(); + + byte[] handle = null; + + try { + handle = nfs.openDirectory(path); + + SftpFile[] list = nfs.readDirectory(handle); + + for (int i = 0; i < list.length; i++) { + writeFileToRemote(path + "/" + list[i].getFilename()); + } + + writeCommand("E"); + } catch (InvalidHandleException ihe) { + throw new IOException(ihe.getMessage()); + } catch (PermissionDeniedException e) { + throw new IOException(e.getMessage()); + } finally { + if (handle != null) { + try { + nfs.closeFile(handle); + } catch (Exception e) { + log.error(e); + } + } + } + + return true; + } + + private void writeFileToRemote(String path) + throws IOException, PermissionDeniedException, InvalidHandleException { + FileAttributes attr = nfs.getFileAttributes(path); + + if (attr.isDirectory()) { + if (!writeDirToRemote(path)) { + return; + } + } else if (attr.isFile()) { + String basename = path; + int idx = basename.lastIndexOf('/'); + + if (idx != -1) { + basename = path.substring(idx + 1); + } + + // TODO: Deal with permissions properly + writeCommand("C" + attr.getMaskString() + " " + attr.getSize() + + " " + basename + "\n"); + waitForResponse(); + log.debug("Opening file " + path); + + byte[] handle = null; + + try { + handle = nfs.openFile(path, + new UnsignedInteger32( + NativeFileSystemProvider.OPEN_READ), attr); + + int count = 0; + log.debug("Sending file"); + + while (count < attr.getSize().intValue()) { + try { + byte[] buf = nfs.readFile(handle, + new UnsignedInteger64(String.valueOf(count)), + new UnsignedInteger32(BUFFER_SIZE)); + count += buf.length; + log.debug("Writing block of " + buf.length + " bytes"); + pipeIn.write(buf); + } catch (EOFException eofe) { + log.debug("End of file - finishing transfer"); + + break; + } + } + + pipeIn.flush(); + + if (count < attr.getSize().intValue()) { + throw new IOException( + "File transfer terminated abnormally."); + } else { + log.info("File transfer complete."); + } + + writeOk(); + } finally { + if (handle != null) { + try { + nfs.closeFile(handle); + } catch (Exception e) { + log.error(e); + } + } + } + } else { + throw new IOException(path + " not valid for SCP."); + } + + waitForResponse(); + } + + private void waitForResponse() throws IOException { + log.debug("Waiting for response"); + + int r = pipeOut.read(); + + if (r == 0) { + log.debug("Got Ok"); + + // All is well, no error + return; + } + + if (r == -1) { + throw new EOFException("SCP returned unexpected EOF"); + } + + String msg = readString(); + log.debug("Got error '" + msg + "'"); + + if (r == (byte) '\02') { + log.debug("This is a serious error"); + throw new IOException(msg); + } + + throw new IOException("SCP returned an unexpected error: " + msg); + } + + private void readFromRemote(String path) throws IOException { + String cmd; + String[] cmdParts = new String[3]; + writeOk(); + + while (true) { + log.debug("Waiting for command"); + + try { + cmd = readString(); + } catch (EOFException e) { + return; + } + + log.debug("Got command '" + cmd + "'"); + + char cmdChar = cmd.charAt(0); + + switch (cmdChar) { + case 'E': + writeOk(); + + return; + + case 'T': + log.error("SCP time not currently supported"); + writeError( + "WARNING: This server does not currently support the SCP time command"); + + break; + + case 'C': + case 'D': + parseCommand(cmd, cmdParts); + + FileAttributes attr = null; + + try { + log.debug("Getting attributes for current destination (" + + path + ")"); + attr = nfs.getFileAttributes(path); + } catch (FileNotFoundException fnfe) { + log.debug("Current destination not found"); + } + + String targetPath = path; + String name = cmdParts[2]; + + if ((attr != null) && attr.isDirectory()) { + log.debug("Target is a directory"); + targetPath += ('/' + name); + } + + FileAttributes targetAttr = null; + + try { + log.debug("Getting attributes for target destination (" + + targetPath + ")"); + targetAttr = nfs.getFileAttributes(targetPath); + } catch (FileNotFoundException fnfe) { + log.debug("Target destination not found"); + } + + if (cmdChar == 'D') { + log.debug("Got directory request"); + + if (targetAttr != null) { + if (!targetAttr.isDirectory()) { + String msg = "Invalid target " + name + + ", must be a directory"; + writeError(msg); + throw new IOException(msg); + } + } else { + try { + log.debug("Creating directory " + targetPath); + + if (!nfs.makeDirectory(targetPath)) { + String msg = "Could not create directory: " + + name; + writeError(msg); + throw new IOException(msg); + } else { + log.debug("Setting permissions on directory"); + attr.setPermissionsFromMaskString(cmdParts[0]); + } + } catch (FileNotFoundException e1) { + writeError("File not found"); + throw new IOException("File not found"); + } catch (PermissionDeniedException e1) { + writeError("Permission denied"); + throw new IOException("Permission denied"); + } + } + + readFromRemote(targetPath); + + continue; + } + + log.debug("Opening file for writing"); + + byte[] handle = null; + + try { + // Open the file + handle = nfs.openFile(targetPath, + new UnsignedInteger32(NativeFileSystemProvider.OPEN_CREATE | + NativeFileSystemProvider.OPEN_WRITE | + NativeFileSystemProvider.OPEN_TRUNCATE), attr); + log.debug("NFS file opened"); + writeOk(); + log.debug("Reading from client"); + + int count = 0; + int read; + long length = Long.parseLong(cmdParts[1]); + + while (count < length) { + read = pipeOut.read(buffer, 0, + (int) (((length - count) < buffer.length) + ? (length - count) : buffer.length)); + + if (read == -1) { + throw new EOFException( + "ScpServer received an unexpected EOF during file transfer"); + } + + log.debug("Got block of " + read); + nfs.writeFile(handle, + new UnsignedInteger64(String.valueOf(count)), + buffer, 0, read); + count += read; + } + + log.debug("File transfer complete"); + } catch (InvalidHandleException ihe) { + writeError("Invalid handle."); + throw new IOException("Invalid handle."); + } catch (FileNotFoundException e) { + writeError("File not found"); + throw new IOException("File not found"); + } catch (PermissionDeniedException e) { + writeError("Permission denied"); + throw new IOException("Permission denied"); + } finally { + if (handle != null) { + try { + log.debug("Closing handle"); + nfs.closeFile(handle); + } catch (Exception e) { + } + } + } + + waitForResponse(); + + if (preserveAttributes) { + attr.setPermissionsFromMaskString(cmdParts[0]); + log.debug("Setting permissions on directory to " + + attr.getPermissionsString()); + + try { + nfs.setFileAttributes(targetPath, attr); + } catch (Exception e) { + writeError("Failed to set file permissions."); + + break; + } + } + + writeOk(); + + break; + + default: + writeError("Unexpected cmd: " + cmd); + throw new IOException("SCP unexpected cmd: " + cmd); + } + } + } + + private void parseCommand(String cmd, String[] cmdParts) + throws IOException { + int l; + int r; + l = cmd.indexOf(' '); + r = cmd.indexOf(' ', l + 1); + + if ((l == -1) || (r == -1)) { + writeError("Syntax error in cmd"); + throw new IOException("Syntax error in cmd"); + } + + cmdParts[0] = cmd.substring(1, l); + cmdParts[1] = cmd.substring(l + 1, r); + cmdParts[2] = cmd.substring(r + 1); + } + + private String readString() throws IOException { + int ch; + int i = 0; + + while (((ch = pipeOut.read()) != ((int) '\n')) && (ch >= 0)) { + buffer[i++] = (byte) ch; + } + + if (ch == -1) { + throw new EOFException("SCP returned unexpected EOF"); + } + + if (buffer[0] == (byte) '\n') { + throw new IOException("Unexpected <NL>"); + } + + if ((buffer[0] == (byte) '\02') || (buffer[0] == (byte) '\01')) { + String msg = new String(buffer, 1, i - 1); + + if (buffer[0] == (byte) '\02') { + throw new IOException(msg); + } + + throw new IOException("SCP returned an unexpected error: " + msg); + } + + return new String(buffer, 0, i); + } +} diff --git a/src/com/sshtools/daemon/session/PseudoTerminalWrapper.java b/src/com/sshtools/daemon/session/PseudoTerminalWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..d78f4d33fad9e0ea88ae2448c8e87e7200e47f2c --- /dev/null +++ b/src/com/sshtools/daemon/session/PseudoTerminalWrapper.java @@ -0,0 +1,128 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.session; + +import com.sshtools.daemon.terminal.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class PseudoTerminalWrapper { + private InputStream masterIn; + private OutputStream masterOut; + private InputStream slaveIn; + private OutputStream slaveOut; + private String term; + private int cols; + private int rows; + private int width; + private int height; + private String modes; + private TerminalIO terminal; + private UserInput ui; + + /** + * Creates a new PseudoTerminalWrapper object. + * + * @param term + * @param cols + * @param rows + * @param width + * @param height + * @param modes + */ + public PseudoTerminalWrapper(String term, int cols, int rows, int width, + int height, String modes) { + this.term = term; + this.cols = cols; + this.rows = rows; + this.height = height; + this.width = width; + } + + /** + * + * + * @param masterIn + */ + public void bindMasterInputStream(InputStream masterIn) { + this.masterIn = masterIn; + } + + /** + * + * + * @param masterOut + */ + public void bindMasterOutputStream(OutputStream masterOut) { + this.masterOut = masterOut; + } + + /** + * + * + * @param slaveOut + */ + public void bindSlaveOutputStream(OutputStream slaveOut) { + this.slaveOut = slaveOut; + } + + /** + * + * + * @param slaveIn + */ + public void bindSlaveInputStream(InputStream slaveIn) { + this.slaveIn = slaveIn; + } + + /** + * + * + * @throws IOException + */ + public void initialize() throws IOException { + this.terminal = new TerminalIO(masterIn, masterOut, term, cols, rows); + terminal.bindSlaveInputStream(slaveIn); + terminal.bindSlaveOutputStream(slaveOut); + ui = new UserInput(terminal, slaveOut); + } + + /** + * + * + * @return + */ + public InputStream getMasterInputStream() { + return terminal.getMasterInputStream(); + } +} diff --git a/src/com/sshtools/daemon/session/SessionChannelFactory.java b/src/com/sshtools/daemon/session/SessionChannelFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d772f6170994d8c445c411fb9e98e41abbfff6af --- /dev/null +++ b/src/com/sshtools/daemon/session/SessionChannelFactory.java @@ -0,0 +1,89 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.session; + +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.connection.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class SessionChannelFactory implements ChannelFactory { + /** */ + public final static String SESSION_CHANNEL = "session"; + Class sessionChannelImpl; + + /** + * Creates a new SessionChannelFactory object. + * + * @throws ConfigurationException + */ + public SessionChannelFactory() throws ConfigurationException { + sessionChannelImpl = SessionChannelServer.class; + + /*ServerConfiguration server = ConfigurationLoader.getServerConfiguration(); +sessionChannelImpl = ConfigurationLoader.getServerConfiguration().getSessionChannelImpl();*/ + } + + /*public List getChannelType() { + List list = new ArrayList(); + list.add(SessionChannelServer.SESSION_CHANNEL_TYPE); + return list; + }*/ + public Channel createChannel(String channelType, byte[] requestData) + throws InvalidChannelException { + try { + if (channelType.equals("session")) { + return (Channel) sessionChannelImpl.newInstance(); + } else { + throw new InvalidChannelException( + "Only session channels can be opened by this factory"); + } + } catch (Exception e) { + throw new InvalidChannelException( + "Failed to create session channel implemented by " + + sessionChannelImpl.getName()); + } + } + + /*public void setSessionChannelImpl(Class sessionChannelImpl) + throws InvalidChannelException { + try { + Channel channel = (Channel) sessionChannelImpl.newInstance(); + if (!(channel instanceof AbstractSessionChannelServer)) { + throw new InvalidChannelException( + "Class does not extend AbstractSessionChannelServer"); + } + } catch (Exception e) { + throw new InvalidChannelException( + "Cannot set session channel implementation"); + } + }*/ +} diff --git a/src/com/sshtools/daemon/session/SessionChannelServer.java b/src/com/sshtools/daemon/session/SessionChannelServer.java new file mode 100644 index 0000000000000000000000000000000000000000..eee0bf1c8e286fbd63ef2b3f1ddbfadf3773b17a --- /dev/null +++ b/src/com/sshtools/daemon/session/SessionChannelServer.java @@ -0,0 +1,603 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.session; + +import com.sshtools.daemon.configuration.*; +import com.sshtools.daemon.platform.*; +import com.sshtools.daemon.scp.*; +import com.sshtools.daemon.subsystem.*; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.agent.*; +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.connection.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.util.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SessionChannelServer extends IOChannel { + private static Log log = LogFactory.getLog(SessionChannelServer.class); + + /** */ + public final static String SESSION_CHANNEL_TYPE = "session"; + private static Map allowedSubsystems = new HashMap(); + private Map environment = new HashMap(); + private NativeProcessProvider processInstance; + private SubsystemServer subsystemInstance; + private Thread thread; + private IOStreamConnector ios; + private ChannelOutputStream stderrOut; + private InputStream stderrIn; + private ProcessMonitorThread processMonitor; + private PseudoTerminalWrapper pty; + private SshAgentForwardingListener agent; + private ServerConfiguration config; + + /** + * Creates a new SessionChannelServer object. + * + * @throws ConfigurationException + */ + public SessionChannelServer() throws ConfigurationException { + super(); + + // Load the allowed subsystems from the server configuration + config = (ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class); + allowedSubsystems.putAll(config.getSubsystems()); + } + + private void bindStderrInputStream(InputStream stderrIn) { + this.stderrIn = stderrIn; + ios = new IOStreamConnector(stderrIn, stderrOut); + } + + /** + * + * + * @param cols + * @param rows + * @param width + * @param height + */ + protected void onChangeTerminalDimensions(int cols, int rows, int width, + int height) { + } + + /** + * + * + * @throws IOException + */ + protected void onChannelClose() throws IOException { + // Remove our reference to the agent + if (agent != null) { + agent.removeReference(this); + } + + if (processInstance != null) { + if (processInstance.stillActive()) { + processInstance.kill(); + } + } + + if (subsystemInstance != null) { + subsystemInstance.stop(); + } + + // If we have a process monitor then get the exit code + // and send before we close the channel + if (processMonitor != null) { + StartStopState state = processMonitor.getStartStopState(); + + try { + state.waitForState(StartStopState.STOPPED); + } catch (InterruptedException ex) { + throw new IOException("The process monitor was interrupted"); + } + } + } + + /** + * + * + * @throws IOException + */ + protected void onChannelEOF() throws IOException { + } + + /** + * + * + * @param data + * + * @throws IOException + */ + protected void onChannelExtData(byte[] data) throws IOException { + // Do something with the data + } + + /** + * + * + * @throws InvalidChannelException + */ + protected void onChannelOpen() throws InvalidChannelException { + stderrOut = new ChannelOutputStream(this, + new Integer(SshMsgChannelExtendedData.SSH_EXTENDED_DATA_STDERR)); + } + + /** + * + * + * @param command + * + * @return + * + * @throws IOException + */ + protected boolean onExecuteCommand(String command) + throws IOException { + log.debug("Executing command " + command); + + // Hack for now + if (command.startsWith("scp ")) { + if (processInstance == null) { + processInstance = new ScpServer(); + } + } + + // Create an instance of the native process provider if we n + if (processInstance == null) { + processInstance = NativeProcessProvider.newInstance(); + } + + if (processInstance == null) { + log.debug("Failed to create process"); + + return false; + } + + boolean result = processInstance.createProcess(command, environment); + + if (result) { + if (pty != null) { + // Bind the streams to the pseudo terminal wrapper + pty.bindMasterOutputStream(getOutputStream()); + pty.bindMasterInputStream(getInputStream()); + pty.bindSlaveInputStream(processInstance.getInputStream()); + pty.bindSlaveOutputStream(processInstance.getOutputStream()); + + // Initialize the terminal + pty.initialize(); + + // Bind the master output stream of the pty to the session + bindInputStream(pty.getMasterInputStream()); + + // Bind the processes stderr + bindStderrInputStream(processInstance.getStderrInputStream()); + } else { + // Just bind the process streams to the session + bindInputStream(processInstance.getInputStream()); + bindOutputStream(processInstance.getOutputStream()); + bindStderrInputStream(processInstance.getStderrInputStream()); + } + } + + return result; + } + + /** + * + * + * @param term + * @param cols + * @param rows + * @param width + * @param height + * @param modes + * + * @return + */ + protected boolean onRequestPseudoTerminal(String term, int cols, int rows, + int width, int height, String modes) { + try { + // Create an instance of the native process provider + processInstance = NativeProcessProvider.newInstance(); + + if (processInstance.supportsPseudoTerminal(term)) { + return processInstance.allocatePseudoTerminal(term, cols, rows, + width, height, modes); + } else { + pty = new PseudoTerminalWrapper(term, cols, rows, width, + height, modes); + + return true; + } + } catch (IOException ioe) { + log.warn("Failed to allocate pseudo terminal " + term, ioe); + + return false; + } + } + + /** + * + * + * @param name + * @param value + */ + protected void onSetEnvironmentVariable(String name, String value) { + environment.put(name, value); + } + + /** + * + * + * @return + * + * @throws IOException + */ + protected boolean onStartShell() throws IOException { + String shell = config.getTerminalProvider(); + + if (processInstance == null) { + processInstance = NativeProcessProvider.newInstance(); + } + + if ((shell != null) && !shell.trim().equals("")) { + int idx = shell.indexOf("%DEFAULT_TERMINAL%"); + + if (idx > -1) { + shell = ((idx > 0) ? shell.substring(0, idx) : "") + + processInstance.getDefaultTerminalProvider() + + (((idx + 18) < shell.length()) ? shell.substring(idx + 18) + : ""); + } + } else { + shell = processInstance.getDefaultTerminalProvider(); + } + + return onExecuteCommand(shell); + } + + /** + * + * + * @param subsystem + * + * @return + */ + protected boolean onStartSubsystem(String subsystem) { + boolean result = false; + + try { + if (!allowedSubsystems.containsKey(subsystem)) { + log.error(subsystem + " Subsystem is not available"); + + return false; + } + + AllowedSubsystem obj = (AllowedSubsystem) allowedSubsystems.get(subsystem); + + if (obj.getType().equals("class")) { + // Create the class implementation and start the subsystem + Class cls = Class.forName(obj.getProvider()); + subsystemInstance = (SubsystemServer) cls.newInstance(); + subsystemInstance.setSession(this); + bindInputStream(subsystemInstance.getInputStream()); + bindOutputStream(subsystemInstance.getOutputStream()); + + return true; + } else { + // Determine the subsystem provider + String provider = obj.getProvider(); + File f = new File(provider); + + if (!f.exists()) { + provider = ConfigurationLoader.getHomeDirectory() + "bin" + + File.separator + provider; + f = new File(provider); + + if (!f.exists()) { + log.error("Failed to locate subsystem provider " + + obj.getProvider()); + + return false; + } + } + + return onExecuteCommand(provider); + } + } catch (Exception e) { + log.error("Failed to start subsystem " + subsystem, e); + } + + return false; + } + + /** + * + * + * @return + */ + public byte[] getChannelOpenData() { + return null; + } + + /** + * + * + * @return + */ + public byte[] getChannelConfirmationData() { + return null; + } + + /** + * + * + * @return + */ + protected int getMinimumWindowSpace() { + return 1024; + } + + /** + * + * + * @return + */ + protected int getMaximumWindowSpace() { + return 32648; + } + + /** + * + * + * @return + */ + protected int getMaximumPacketSize() { + return 32648; + } + + /** + * + * + * @return + */ + public String getChannelType() { + return SESSION_CHANNEL_TYPE; + } + + /** + * + * + * @param requestType + * @param wantReply + * @param requestData + * + * @throws IOException + */ + protected void onChannelRequest(String requestType, boolean wantReply, + byte[] requestData) throws IOException { + log.debug("Channel Request received: " + requestType); + + boolean success = false; + + if (requestType.equals("shell")) { + success = onStartShell(); + + if (success) { + if (wantReply) { + connection.sendChannelRequestSuccess(this); + } + + processInstance.start(); + processMonitor = new ProcessMonitorThread(processInstance); + } else if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + + if (requestType.equals("env")) { + ByteArrayReader bar = new ByteArrayReader(requestData); + String name = bar.readString(); + String value = bar.readString(); + onSetEnvironmentVariable(name, value); + + if (wantReply) { + connection.sendChannelRequestSuccess(this); + } + } + + if (requestType.equals("exec")) { + ByteArrayReader bar = new ByteArrayReader(requestData); + String command = bar.readString(); + success = onExecuteCommand(command); + + if (success) { + if (wantReply) { + connection.sendChannelRequestSuccess(this); + } + + processInstance.start(); + processMonitor = new ProcessMonitorThread(processInstance); + } else if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + + if (requestType.equals("subsystem")) { + ByteArrayReader bar = new ByteArrayReader(requestData); + String subsystem = bar.readString(); + success = onStartSubsystem(subsystem); + + if (success) { + if (wantReply) { + connection.sendChannelRequestSuccess(this); + } + + if (processInstance != null) { + processInstance.start(); + processMonitor = new ProcessMonitorThread(processInstance); + } else if (subsystemInstance != null) { + subsystemInstance.start(); + processMonitor = new ProcessMonitorThread(subsystemInstance); + } + } else if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + + if (requestType.equals("pty-req")) { + ByteArrayReader bar = new ByteArrayReader(requestData); + String term = bar.readString(); + int cols = (int) bar.readInt(); + int rows = (int) bar.readInt(); + int width = (int) bar.readInt(); + int height = (int) bar.readInt(); + String modes = bar.readString(); + success = onRequestPseudoTerminal(term, cols, rows, width, height, + modes); + + if (wantReply && success) { + connection.sendChannelRequestSuccess(this); + } else if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + + if (requestType.equals("window-change")) { + ByteArrayReader bar = new ByteArrayReader(requestData); + int cols = (int) bar.readInt(); + int rows = (int) bar.readInt(); + int width = (int) bar.readInt(); + int height = (int) bar.readInt(); + onChangeTerminalDimensions(cols, rows, width, height); + + if (wantReply && success) { + connection.sendChannelRequestSuccess(this); + } else if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + + if (requestType.equals("auth-agent-req")) { + try { + SshThread thread = SshThread.getCurrentThread(); + + // Get an agent instance + agent = SshAgentForwardingListener.getInstance(thread.getSessionIdString(), + connection); + + // Inform the agent we want to track this reference + agent.addReference(this); + + // Set the environment so processes can find the agent + environment.put("SSH_AGENT_AUTH", agent.getConfiguration()); + + // Set a thread property so other services within this server can find it + thread.setProperty("sshtools.agent", agent.getConfiguration()); + + if (wantReply) { + connection.sendChannelRequestSuccess(this); + } + } catch (Exception ex) { + if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + } + } + + class ProcessMonitorThread extends Thread { + private NativeProcessProvider process; + private SubsystemServer subsystem; + private StartStopState state; + + public ProcessMonitorThread(NativeProcessProvider process) { + this.process = process; + state = new StartStopState(StartStopState.STARTED); + start(); + } + + public ProcessMonitorThread(SubsystemServer subsystem) { + state = subsystem.getState(); + } + + public StartStopState getStartStopState() { + return state; + } + + public void run() { + try { + log.info("Monitor waiting for process exit code"); + + int exitcode = process.waitForExitCode(); + + if (exitcode == 9999999) { + log.error("Process monitor failed to retrieve exit code"); + } else { + log.debug("Process exit code is " + + String.valueOf(exitcode)); + process.getInputStream().close(); + process.getOutputStream().close(); + process.getStderrInputStream().close(); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeInt(exitcode); + + // Send the exit request + if (connection.isConnected() && + SessionChannelServer.this.isOpen()) { + connection.sendChannelRequest(SessionChannelServer.this, + "exit-status", false, baw.toByteArray()); + } + + // Stop the monitor + state.setValue(StartStopState.STOPPED); + + // Close the session + SessionChannelServer.this.close(); + } + } catch (IOException ioe) { + log.error("Failed to kill process", ioe); + } + } + } +} diff --git a/src/com/sshtools/daemon/sftp/SftpSubsystemServer.java b/src/com/sshtools/daemon/sftp/SftpSubsystemServer.java new file mode 100644 index 0000000000000000000000000000000000000000..644e5848b40cad066baafa0d9af07620c9f4485f --- /dev/null +++ b/src/com/sshtools/daemon/sftp/SftpSubsystemServer.java @@ -0,0 +1,777 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.sftp; + +import com.sshtools.daemon.platform.*; +import com.sshtools.daemon.session.*; +import com.sshtools.daemon.subsystem.*; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.connection.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.sftp.*; +import com.sshtools.j2ssh.subsystem.*; + +import org.apache.commons.logging.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SftpSubsystemServer extends SubsystemServer { + /** */ + public static final int VERSION_1 = 1; + + /** */ + public static final int VERSION_2 = 2; + + /** */ + public static final int VERSION_3 = 3; + + /** */ + public static final int VERSION_4 = 4; + private static Log log = LogFactory.getLog(SftpSubsystemServer.class); + private NativeFileSystemProvider nfs; + + /** + * Creates a new SftpSubsystemServer object. + */ + public SftpSubsystemServer() { + registerMessage(SshFxpInit.SSH_FXP_INIT, SshFxpInit.class); + registerMessage(SshFxpMkdir.SSH_FXP_MKDIR, SshFxpMkdir.class); + registerMessage(SshFxpRealPath.SSH_FXP_REALPATH, SshFxpRealPath.class); + registerMessage(SshFxpOpenDir.SSH_FXP_OPENDIR, SshFxpOpenDir.class); + registerMessage(SshFxpOpen.SSH_FXP_OPEN, SshFxpOpen.class); + registerMessage(SshFxpRead.SSH_FXP_READ, SshFxpRead.class); + registerMessage(SshFxpWrite.SSH_FXP_WRITE, SshFxpWrite.class); + registerMessage(SshFxpReadDir.SSH_FXP_READDIR, SshFxpReadDir.class); + registerMessage(SshFxpClose.SSH_FXP_CLOSE, SshFxpClose.class); + registerMessage(SshFxpLStat.SSH_FXP_LSTAT, SshFxpLStat.class); + registerMessage(SshFxpStat.SSH_FXP_STAT, SshFxpStat.class); + registerMessage(SshFxpRemove.SSH_FXP_REMOVE, SshFxpRemove.class); + registerMessage(SshFxpRename.SSH_FXP_RENAME, SshFxpRename.class); + registerMessage(SshFxpRmdir.SSH_FXP_RMDIR, SshFxpRmdir.class); + registerMessage(SshFxpSetStat.SSH_FXP_SETSTAT, SshFxpSetStat.class); + registerMessage(SshFxpFStat.SSH_FXP_FSTAT, SshFxpFStat.class); + registerMessage(SshFxpFSetStat.SSH_FXP_FSETSTAT, SshFxpFSetStat.class); + registerMessage(SshFxpReadlink.SSH_FXP_READLINK, SshFxpReadlink.class); + registerMessage(SshFxpSymlink.SSH_FXP_SYMLINK, SshFxpSymlink.class); + } + + /** + * + * + * @param session + */ + public void setSession(SessionChannelServer session) { + session.addEventListener(new ChannelEventListener() { + public void onChannelOpen(Channel channel) { + } + + public void onChannelEOF(Channel channel) { + try { + SftpSubsystemServer.this.session.close(); + } catch (IOException ex) { + } + } + + public void onChannelClose(Channel channel) { + } + + public void onDataReceived(Channel channel, byte[] data) { + } + + public void onDataSent(Channel channel, byte[] data) { + } + }); + super.setSession(session); + } + + /** + * + * + * @param msg + */ + protected void onMessageReceived(SubsystemMessage msg) { + switch (msg.getMessageType()) { + case SshFxpInit.SSH_FXP_INIT: { + onInitialize((SshFxpInit) msg); + + break; + } + + case SshFxpMkdir.SSH_FXP_MKDIR: { + onMakeDirectory((SshFxpMkdir) msg); + + break; + } + + case SshFxpRealPath.SSH_FXP_REALPATH: { + onRealPath((SshFxpRealPath) msg); + + break; + } + + case SshFxpOpenDir.SSH_FXP_OPENDIR: { + onOpenDirectory((SshFxpOpenDir) msg); + + break; + } + + case SshFxpOpen.SSH_FXP_OPEN: { + onOpenFile((SshFxpOpen) msg); + + break; + } + + case SshFxpRead.SSH_FXP_READ: { + onReadFile((SshFxpRead) msg); + + break; + } + + case SshFxpWrite.SSH_FXP_WRITE: { + onWriteFile((SshFxpWrite) msg); + + break; + } + + case SshFxpReadDir.SSH_FXP_READDIR: { + onReadDirectory((SshFxpReadDir) msg); + + break; + } + + case SshFxpLStat.SSH_FXP_LSTAT: { + onLStat((SshFxpLStat) msg); + + break; + } + + case SshFxpStat.SSH_FXP_STAT: { + onStat((SshFxpStat) msg); + + break; + } + + case SshFxpFStat.SSH_FXP_FSTAT: { + onFStat((SshFxpFStat) msg); + + break; + } + + case SshFxpClose.SSH_FXP_CLOSE: { + onCloseFile((SshFxpClose) msg); + + break; + } + + case SshFxpRemove.SSH_FXP_REMOVE: { + onRemoveFile((SshFxpRemove) msg); + + break; + } + + case SshFxpRename.SSH_FXP_RENAME: { + onRenameFile((SshFxpRename) msg); + + break; + } + + case SshFxpRmdir.SSH_FXP_RMDIR: { + onRemoveDirectory((SshFxpRmdir) msg); + + break; + } + + case SshFxpSetStat.SSH_FXP_SETSTAT: { + onSetAttributes((SshFxpSetStat) msg); + + break; + } + + case SshFxpFSetStat.SSH_FXP_FSETSTAT: { + onSetAttributes((SshFxpFSetStat) msg); + + break; + } + + case SshFxpReadlink.SSH_FXP_READLINK: { + onReadlink((SshFxpReadlink) msg); + + break; + } + + case SshFxpSymlink.SSH_FXP_SYMLINK: { + onSymlink((SshFxpSymlink) msg); + + break; + } + + default: { + } + } + } + + private void onSetAttributes(SshFxpSetStat msg) { + SubsystemMessage reply; + + try { + nfs.setFileAttributes(checkDefaultPath(msg.getPath()), + msg.getAttributes()); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The attributes were set", ""); + } catch (FileNotFoundException fnfe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + fnfe.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } catch (IOException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onSetAttributes(SshFxpFSetStat msg) { + SubsystemMessage reply; + + try { + nfs.setFileAttributes(msg.getHandle(), msg.getAttributes()); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The attributes were set", ""); + } catch (InvalidHandleException ihe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ihe.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } catch (IOException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onReadlink(SshFxpReadlink msg) { + SubsystemMessage reply; + + try { + /* + File f = nfs.readSymbolicLink(VirtualFileSystem.translateVFSPath( + msg.getPath())); + SftpFile[] files = new SftpFile[1]; + files[0] = new SftpFile(VirtualFileSystem.translateNFSPath( + f.getCanonicalPath()), + nfs.getFileAttributes(f.getCanonicalPath())); + reply = new SshFxpName(msg.getId(), files); + */ + reply = new SshFxpName(msg.getId(), + new SftpFile[] { + nfs.readSymbolicLink(checkDefaultPath(msg.getPath())) + }); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } catch (UnsupportedFileOperationException uso) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OP_UNSUPPORTED), + uso.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onSymlink(SshFxpSymlink msg) { + SubsystemMessage reply; + + try { + /* + nfs.createSymbolicLink(VirtualFileSystem.translateVFSPath( + msg.getLinkPath()), + VirtualFileSystem.translateVFSPath(msg.getTargetPath())); + */ + nfs.createSymbolicLink(checkDefaultPath(msg.getLinkPath()), + checkDefaultPath(msg.getTargetPath())); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The symbolic link was created", ""); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } catch (UnsupportedFileOperationException uso) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OP_UNSUPPORTED), + uso.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onRemoveDirectory(SshFxpRmdir msg) { + SubsystemMessage reply; + + try { + /* + nfs.removeDirectory(VirtualFileSystem.translateVFSPath( + msg.getPath())); + */ + nfs.removeDirectory(checkDefaultPath(msg.getPath())); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The directory was removed", ""); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onRenameFile(SshFxpRename msg) { + SubsystemMessage reply; + + try { + /* + nfs.renameFile(VirtualFileSystem.translateVFSPath(msg.getOldPath()), + VirtualFileSystem.translateVFSPath(msg.getNewPath())); + */ + nfs.renameFile(checkDefaultPath(msg.getOldPath()), + checkDefaultPath(msg.getNewPath())); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The file was removed", ""); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onRemoveFile(SshFxpRemove msg) { + SubsystemMessage reply; + + try { + /* + nfs.removeFile(VirtualFileSystem.translateVFSPath(msg.getFilename())); + */ + nfs.removeFile(checkDefaultPath(msg.getFilename())); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The file was removed", ""); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onOpenFile(SshFxpOpen msg) { + SubsystemMessage reply; + + try { + reply = new SshFxpHandle(msg.getId(), + nfs.openFile(checkDefaultPath(msg.getFilename()), + msg.getPflags(), msg.getAttributes())); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onReadFile(SshFxpRead msg) { + SubsystemMessage reply; + + try { + reply = new SshFxpData(msg.getId(), + nfs.readFile(msg.getHandle(), msg.getOffset(), + msg.getLength())); + } catch (EOFException eof) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_EOF), + eof.getMessage(), ""); + } catch (InvalidHandleException ihe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ihe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onWriteFile(SshFxpWrite msg) { + SubsystemMessage reply; + + try { + nfs.writeFile(msg.getHandle(), msg.getOffset(), msg.getData(), 0, + msg.getData().length); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The write completed successfully", ""); + } catch (InvalidHandleException ihe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ihe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onCloseFile(SshFxpClose msg) { + SubsystemMessage reply; + + try { + nfs.closeFile(msg.getHandle()); + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The operation completed", ""); + } catch (InvalidHandleException ihe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ihe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onFStat(SshFxpFStat msg) { + SubsystemMessage reply; + + try { + reply = new SshFxpAttrs(msg.getId(), + nfs.getFileAttributes(msg.getHandle())); + } catch (InvalidHandleException ihe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ihe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onStat(SshFxpStat msg) { + SubsystemMessage reply; + + try { + String path = checkDefaultPath(msg.getPath()); + + if (nfs.fileExists(path)) { + SftpFile[] files = new SftpFile[1]; + reply = new SshFxpAttrs(msg.getId(), + nfs.getFileAttributes( + /*nfs.getCanonicalPath(*/ + msg.getPath() /*)*/)); + } else { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + path + " is not a valid file path", ""); + } + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onLStat(SshFxpLStat msg) { + SubsystemMessage reply; + + try { + String path = checkDefaultPath(msg.getPath()); + + if (nfs.fileExists(path)) { + SftpFile[] files = new SftpFile[1]; + reply = new SshFxpAttrs(msg.getId(), + nfs.getFileAttributes(nfs.getCanonicalPath( + msg.getPath()))); + } else { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + path + " is not a valid file path", ""); + } + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onReadDirectory(SshFxpReadDir msg) { + SubsystemMessage reply; + + try { + /* + File[] files = nfs.readDirectory(msg.getHandle()); + SftpFile[] sftpfiles = new SftpFile[files.length]; + for (int i = 0; i < files.length; i++) { + sftpfiles[i] = new SftpFile(files[i].getName(), + nfs.getFileAttributes(files[i].getCanonicalPath())); + } + */ + SftpFile[] sftpfiles = nfs.readDirectory(msg.getHandle()); + reply = new SshFxpName(msg.getId(), sftpfiles); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (InvalidHandleException ihe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ihe.getMessage(), ""); + } catch (EOFException eof) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_EOF), + eof.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onOpenDirectory(SshFxpOpenDir msg) { + SubsystemMessage reply; + + try { + /* + String path = VirtualFileSystem.translateVFSPath(msg.getPath()); + */ + String path = checkDefaultPath(msg.getPath()); + reply = new SshFxpHandle(msg.getId(), nfs.openDirectory(path)); + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe2) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe2.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onRealPath(SshFxpRealPath msg) { + SubsystemMessage reply; + + try { + /* + String path = VirtualFileSystem.translateVFSPath(msg.getPath()); + path = VirtualFileSystem.translateNFSPath(path); + */ + String path = nfs.getRealPath(checkDefaultPath(msg.getPath())); + + if (path != null) { + SftpFile[] files = new SftpFile[1]; + files[0] = new SftpFile(path); + reply = new SshFxpName(msg.getId(), files); + } else { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + msg.getPath() + + " could not be translated into a system dependent path", + ""); + } + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (IOException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe.getMessage(), ""); + } + + sendMessage(reply); + } + + private void onMakeDirectory(SshFxpMkdir msg) { + SubsystemMessage reply; + + try { + /* + String path = VirtualFileSystem.translateVFSPath(msg.getPath()); + */ + String path = checkDefaultPath(msg.getPath()); + + if (nfs.makeDirectory(path)) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_OK), + "The operation completed sucessfully", ""); + } else { + // Send an error back to the client + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + "The operation failed", ""); + } + } catch (FileNotFoundException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_NO_SUCH_FILE), + ioe.getMessage(), ""); + } catch (PermissionDeniedException pde) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_PERMISSION_DENIED), + pde.getMessage(), ""); + } catch (IOException ioe) { + reply = new SshFxpStatus(msg.getId(), + new UnsignedInteger32(SshFxpStatus.STATUS_FX_FAILURE), + ioe.getMessage(), ""); + } + + sendMessage(reply); + } + + private String checkDefaultPath(String path) throws IOException { + // Use the users home directory if no path is supplied + if (path.equals("")) { + return nfs.getDefaultPath(SshThread.getCurrentThreadUser()); + } else { + return path; + } + } + + private void onInitialize(SshFxpInit msg) { + // Get the native file system + nfs = NativeFileSystemProvider.getInstance(); + + // Determine the users home directory + if (msg.getVersion().intValue() == VERSION_3) { + SshFxpVersion reply = new SshFxpVersion(new UnsignedInteger32( + VERSION_3), null); + sendMessage(reply); + } else { + // Wrong version + } + } +} diff --git a/src/com/sshtools/daemon/subsystem/SubsystemServer.java b/src/com/sshtools/daemon/subsystem/SubsystemServer.java new file mode 100644 index 0000000000000000000000000000000000000000..04ba208a2457e35b3242f3667752f472a9c405a9 --- /dev/null +++ b/src/com/sshtools/daemon/subsystem/SubsystemServer.java @@ -0,0 +1,173 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.subsystem; + +import com.sshtools.daemon.session.*; + +import com.sshtools.j2ssh.*; +import com.sshtools.j2ssh.subsystem.*; +import com.sshtools.j2ssh.transport.*; +import com.sshtools.j2ssh.util.*; + +import org.apache.commons.logging.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public abstract class SubsystemServer implements Runnable { + private static Log log = LogFactory.getLog(SubsystemServer.class); + private SubsystemMessageStore incoming = new SubsystemMessageStore(); + private SubsystemMessageStore outgoing = new SubsystemMessageStore(); + private SubsystemInputStream in = new SubsystemInputStream(outgoing); + private SubsystemOutputStream out = new SubsystemOutputStream(incoming); + private SshThread thread; + private StartStopState state = new StartStopState(StartStopState.STOPPED); + + /** */ + protected SessionChannelServer session; + + /** + * Creates a new SubsystemServer object. + */ + public SubsystemServer() { + } + + /** + * + * + * @param session + */ + public void setSession(SessionChannelServer session) { + this.session = session; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public InputStream getInputStream() throws IOException { + return in; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public OutputStream getOutputStream() throws IOException { + return out; + } + + /** + * + */ + public void run() { + state.setValue(StartStopState.STARTED); + + try { + while (state.getValue() == StartStopState.STARTED) { + SubsystemMessage msg = incoming.nextMessage(); + + if (msg != null) { + onMessageReceived(msg); + } + } + } catch (MessageStoreEOFException meof) { + } + + thread = null; + } + + /** + * + */ + public void start() { + if (Thread.currentThread() instanceof SshThread) { + thread = ((SshThread) Thread.currentThread()).cloneThread(this, + "SubsystemServer"); + thread.start(); + } else { + log.error( + "Subsystem Server must be called from within an SshThread context"); + stop(); + } + } + + /** + * + */ + public void stop() { + state.setValue(StartStopState.STOPPED); + incoming.close(); + outgoing.close(); + } + + /** + * + * + * @return + */ + public StartStopState getState() { + return state; + } + + /** + * + * + * @param msg + */ + protected abstract void onMessageReceived(SubsystemMessage msg); + + /** + * + * + * @param messageId + * @param implementor + */ + protected void registerMessage(int messageId, Class implementor) { + incoming.registerMessage(messageId, implementor); + } + + /** + * + * + * @param msg + */ + protected void sendMessage(SubsystemMessage msg) { + outgoing.addMessage(msg); + } +} diff --git a/src/com/sshtools/daemon/terminal/BasicTerminal.java b/src/com/sshtools/daemon/terminal/BasicTerminal.java new file mode 100644 index 0000000000000000000000000000000000000000..b726fcb9033a8273efa42649515394b6b94d1633 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/BasicTerminal.java @@ -0,0 +1,399 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public abstract class BasicTerminal implements Terminal { + //Associations + + /** */ + protected Colorizer myColorizer; + + /** + * Creates a new BasicTerminal object. + */ + public BasicTerminal() { + myColorizer = Colorizer.getReference(); + } + + //constructor + public int translateControlCharacter(int c) { + switch (c) { + case DEL: + return TerminalIO.DELETE; + + case BS: + return TerminalIO.BACKSPACE; + + case HT: + return TerminalIO.TABULATOR; + + case ESC: + return TerminalIO.ESCAPE; + + case SGR: + return TerminalIO.COLORINIT; + + case EOT: + return TerminalIO.LOGOUTREQUEST; + + default: + return c; + } + } + + //translateControlCharacter + public int translateEscapeSequence(int[] buffer) { + try { + if (buffer[0] == LSB) { + switch (buffer[1]) { + case A: + return TerminalIO.UP; + + case B: + return TerminalIO.DOWN; + + case C: + return TerminalIO.RIGHT; + + case D: + return TerminalIO.LEFT; + + default: + break; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + return TerminalIO.BYTEMISSING; + } + + return TerminalIO.UNRECOGNIZED; + } + + //translateEscapeSequence + public byte[] getCursorMoveSequence(int direction, int times) { + byte[] sequence = null; + + if (times == 1) { + sequence = new byte[3]; + } else { + sequence = new byte[times * 3]; + } + + for (int g = 0; g < (times * 3); g++) { + sequence[g] = ESC; + sequence[g + 1] = LSB; + + switch (direction) { + case TerminalIO.UP: + sequence[g + 2] = A; + + break; + + case TerminalIO.DOWN: + sequence[g + 2] = B; + + break; + + case TerminalIO.RIGHT: + sequence[g + 2] = C; + + break; + + case TerminalIO.LEFT: + sequence[g + 2] = D; + + break; + + default: + break; + } + + g = g + 2; + } + + return sequence; + } + + // getCursorMoveSequence + public byte[] getCursorPositioningSequence(int[] pos) { + byte[] sequence = null; + + if (pos == TerminalIO.HOME) { + sequence = new byte[3]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = H; + } else { + //first translate integer coords into digits + byte[] rowdigits = translateIntToDigitCodes(pos[0]); + byte[] columndigits = translateIntToDigitCodes(pos[1]); + int offset = 0; + + //now build up the sequence: + sequence = new byte[4 + rowdigits.length + columndigits.length]; + sequence[0] = ESC; + sequence[1] = LSB; + + //now copy the digit bytes + System.arraycopy(rowdigits, 0, sequence, 2, rowdigits.length); + + //offset is now 2+rowdigits.length + offset = 2 + rowdigits.length; + sequence[offset] = SEMICOLON; + offset++; + System.arraycopy(columndigits, 0, sequence, offset, + columndigits.length); + offset = offset + columndigits.length; + sequence[offset] = H; + } + + return sequence; + } + + //getCursorPositioningSequence + public byte[] getEraseSequence(int eraseFunc) { + byte[] sequence = null; + + switch (eraseFunc) { + case TerminalIO.EEOL: + sequence = new byte[3]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = LE; + + break; + + case TerminalIO.EBOL: + sequence = new byte[4]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = 49; //Ascii Code of 1 + sequence[3] = LE; + + break; + + case TerminalIO.EEL: + sequence = new byte[4]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = 50; //Ascii Code 2 + sequence[3] = LE; + + break; + + case TerminalIO.EEOS: + sequence = new byte[3]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = SE; + + break; + + case TerminalIO.EBOS: + sequence = new byte[4]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = 49; //Ascii Code of 1 + sequence[3] = SE; + + break; + + case TerminalIO.EES: + sequence = new byte[4]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = 50; //Ascii Code of 2 + sequence[3] = SE; + + break; + + default: + break; + } + + return sequence; + } + + //getEraseSequence + public byte[] getSpecialSequence(int function) { + byte[] sequence = null; + + switch (function) { + case TerminalIO.STORECURSOR: + sequence = new byte[2]; + sequence[0] = ESC; + sequence[1] = 55; //Ascii Code of 7 + + break; + + case TerminalIO.RESTORECURSOR: + sequence = new byte[2]; + sequence[0] = ESC; + sequence[1] = 56; //Ascii Code of 8 + + break; + } + + return sequence; + } + + //getSpecialSequence + public byte[] getGRSequence(int type, int param) { + byte[] sequence = new byte[0]; + int offset = 0; + + switch (type) { + case TerminalIO.FCOLOR: + case TerminalIO.BCOLOR: + + byte[] color = translateIntToDigitCodes(param); + sequence = new byte[3 + color.length]; + sequence[0] = ESC; + sequence[1] = LSB; + + //now copy the digit bytes + System.arraycopy(color, 0, sequence, 2, color.length); + + //offset is now 2+color.length + offset = 2 + color.length; + sequence[offset] = 109; //ASCII Code of m + + break; + + case TerminalIO.STYLE: + + byte[] style = translateIntToDigitCodes(param); + sequence = new byte[3 + style.length]; + sequence[0] = ESC; + sequence[1] = LSB; + + //now copy the digit bytes + System.arraycopy(style, 0, sequence, 2, style.length); + + //offset is now 2+style.length + offset = 2 + style.length; + sequence[offset] = 109; //ASCII Code of m + + break; + + case TerminalIO.RESET: + sequence = new byte[5]; + sequence[0] = ESC; + sequence[1] = LSB; + sequence[2] = 52; //ASCII Code of 4 + sequence[3] = 56; //ASCII Code of 8 + sequence[4] = 109; //ASCII Code of m + + break; + } + + return sequence; + } + + //getGRsequence + public byte[] getScrollMarginsSequence(int topmargin, int bottommargin) { + byte[] sequence = new byte[0]; + + if (supportsScrolling()) { + //first translate integer coords into digits + byte[] topdigits = translateIntToDigitCodes(topmargin); + byte[] bottomdigits = translateIntToDigitCodes(bottommargin); + int offset = 0; + + //now build up the sequence: + sequence = new byte[4 + topdigits.length + bottomdigits.length]; + sequence[0] = ESC; + sequence[1] = LSB; + + //now copy the digit bytes + System.arraycopy(topdigits, 0, sequence, 2, topdigits.length); + + //offset is now 2+topdigits.length + offset = 2 + topdigits.length; + sequence[offset] = SEMICOLON; + offset++; + System.arraycopy(bottomdigits, 0, sequence, offset, + bottomdigits.length); + offset = offset + bottomdigits.length; + sequence[offset] = r; + } + + return sequence; + } + + //getScrollMarginsSequence + public String format(String str) { + return myColorizer.colorize(str, supportsSGR()); + } + + //format + public byte[] getInitSequence() { + byte[] sequence = new byte[0]; + + return sequence; + } + + //getInitSequence + public int getAtomicSequenceLength() { + return 2; + } + + //getAtomicSequenceLength + public byte[] translateIntToDigitCodes(int in) { + return Integer.toString(in).getBytes(); + } + + //translateIntToDigitCodes + public abstract boolean supportsSGR(); + + /** + * + * + * @return + */ + public abstract boolean supportsScrolling(); +} + + +//class BasicTerminal diff --git a/src/com/sshtools/daemon/terminal/BufferOverflowException.java b/src/com/sshtools/daemon/terminal/BufferOverflowException.java new file mode 100644 index 0000000000000000000000000000000000000000..fac4a58f9433ab39620407ea8c3d342e93d2d440 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/BufferOverflowException.java @@ -0,0 +1,50 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class BufferOverflowException extends Exception { +} + + +//class BufferOverFlowException diff --git a/src/com/sshtools/daemon/terminal/CharBuffer.java b/src/com/sshtools/daemon/terminal/CharBuffer.java new file mode 100644 index 0000000000000000000000000000000000000000..ca71df6e5b81bca84f646d0d6a4f8c2c9457bea1 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/CharBuffer.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + +import java.util.*; + + +class CharBuffer { + //Members + private Vector myBuffer; + private int mySize; + + /** + * Creates a new CharBuffer object. + * + * @param size + */ + public CharBuffer(int size) { + myBuffer = new Vector(size); + mySize = size; + } + + //constructor + public char getCharAt(int pos) throws IndexOutOfBoundsException { + return ((Character) myBuffer.elementAt(pos)).charValue(); + } + + //getCharAt + public void setCharAt(int pos, char ch) throws IndexOutOfBoundsException { + myBuffer.setElementAt(new Character(ch), pos); + } + + //setCharAt + public void insertCharAt(int pos, char ch) + throws BufferOverflowException, IndexOutOfBoundsException { + myBuffer.insertElementAt(new Character(ch), pos); + } + + //insertCharAt + public void append(char aChar) throws BufferOverflowException { + myBuffer.addElement(new Character(aChar)); + } + + //append + public void removeCharAt(int pos) throws IndexOutOfBoundsException { + myBuffer.removeElementAt(pos); + } + + //removeCharAt + public void clear() { + myBuffer.removeAllElements(); + } + + //clear + public int size() { + return myBuffer.size(); + } + + //size + public String toString() { + StringBuffer sbuf = new StringBuffer(); + + for (int i = 0; i < myBuffer.size(); i++) { + sbuf.append(((Character) myBuffer.elementAt(i)).charValue()); + } + + return sbuf.toString(); + } + + //toString + public void ensureSpace(int chars) throws BufferOverflowException { + if (chars > (mySize - myBuffer.size())) { + throw new BufferOverflowException(); + } + } + + //ensureSpace +} + + +//class CharBuffer diff --git a/src/com/sshtools/daemon/terminal/ColorHelper.java b/src/com/sshtools/daemon/terminal/ColorHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..13684d1ab61ca06139c19fae84cae04e2036a231 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/ColorHelper.java @@ -0,0 +1,190 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class ColorHelper { + /** */ + public static final String INTERNAL_MARKER = "\001"; + + /** */ + public static final int MARKER_CODE = 1; + + /** */ + public static final String BLACK = "S"; + + /** */ + public static final String RED = "R"; + + /** */ + public static final String GREEN = "G"; + + /** */ + public static final String YELLOW = "Y"; + + /** */ + public static final String BLUE = "B"; + + /** */ + public static final String MAGENTA = "M"; + + /** */ + public static final String CYAN = "C"; + + /** */ + public static final String white = "W"; + + /** */ + public static final String BOLD = "f"; + + /** */ + public static final String BOLD_OFF = "d"; //normal color or normal intensity + + /** */ + public static final String ITALIC = "i"; + + /** */ + public static final String ITALIC_OFF = "j"; + + /** */ + public static final String UNDERLINED = "u"; + + /** */ + public static final String UNDERLINED_OFF = "v"; + + /** */ + public static final String BLINK = "e"; + + /** */ + public static final String BLINK_OFF = "n"; + + /** */ + public static final String RESET_ALL = "a"; + + /** + * + * + * @param str + * @param color + * + * @return + */ + public static String colorizeText(String str, String color) { + return INTERNAL_MARKER + color + str + INTERNAL_MARKER + RESET_ALL; + } + + //colorizeText + public static String colorizeBackground(String str, String color) { + return INTERNAL_MARKER + color.toLowerCase() + str + INTERNAL_MARKER + + RESET_ALL; + } + + //colorizeBackground + public static String colorizeText(String str, String fgc, String bgc) { + return INTERNAL_MARKER + fgc + INTERNAL_MARKER + bgc.toLowerCase() + + str + INTERNAL_MARKER + RESET_ALL; + } + + //colorizeText + public static String boldcolorizeText(String str, String color) { + return INTERNAL_MARKER + BOLD + INTERNAL_MARKER + color + str + + INTERNAL_MARKER + RESET_ALL; + } + + //colorizeBoldText + public static String boldcolorizeText(String str, String fgc, String bgc) { + return INTERNAL_MARKER + BOLD + INTERNAL_MARKER + fgc + + INTERNAL_MARKER + bgc.toLowerCase() + str + INTERNAL_MARKER + + RESET_ALL; + } + + //colorizeBoldText + public static String boldText(String str) { + return INTERNAL_MARKER + BOLD + str + INTERNAL_MARKER + BOLD_OFF; + } + + //boldText + public static String italicText(String str) { + return INTERNAL_MARKER + ITALIC + str + INTERNAL_MARKER + ITALIC_OFF; + } + + //italicText + public static String underlinedText(String str) { + return INTERNAL_MARKER + UNDERLINED + str + INTERNAL_MARKER + + UNDERLINED_OFF; + } + + //underlinedText + public static String blinkingText(String str) { + return INTERNAL_MARKER + BLINK + str + INTERNAL_MARKER + BLINK_OFF; + } + + //blinkingText + public static long getVisibleLength(String str) { + int counter = 0; + int parsecursor = 0; + int foundcursor = 0; + boolean done = false; + + while (!done) { + foundcursor = str.indexOf(MARKER_CODE, parsecursor); + + if (foundcursor != -1) { + //increment counter + counter++; + + //parseon from the next char + parsecursor = foundcursor + 1; + } else { + done = true; + } + } + + return (str.length() - (counter * 2)); + } + + //getVisibleLength +} + + +//class ColorHelper diff --git a/src/com/sshtools/daemon/terminal/Colorizer.java b/src/com/sshtools/daemon/terminal/Colorizer.java new file mode 100644 index 0000000000000000000000000000000000000000..832f0bd3d8cbd113a55acf8983649451eaafcee9 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/Colorizer.java @@ -0,0 +1,372 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public final class Colorizer { + private static Object Self; //Singleton instance reference + private static int testcount = 0; + private static Colorizer myColorizer; + + //Constants + private static final int + /*black*/ S = 30; + + //Constants + private static final int s = 40; + + //Constants + private static final int + /*red*/ R = 31; + + //Constants + private static final int r = 41; + + //Constants + private static final int + /*green*/ G = 32; + + //Constants + private static final int g = 42; + + //Constants + private static final int + /*yellow*/ Y = 33; + + //Constants + private static final int y = 43; + + //Constants + private static final int + /*blue*/ B = 34; + + //Constants + private static final int b = 44; + + //Constants + private static final int + /*magenta*/ M = 35; + + //Constants + private static final int m = 45; + + //Constants + private static final int + /*cyan*/ C = 36; + + //Constants + private static final int c = 46; + + //Constants + private static final int + /*white*/ W = 37; + + //Constants + private static final int w = 47; + + //Constants + private static final int + /*bold*/ f = 1; + + //Constants + private static final int + /*!bold*/ d = 22; + + //Constants + private static final int + /*italic*/ i = 3; + + //Constants + private static final int + /*!italic*/ j = 23; + + //Constants + private static final int + /*underlined*/ u = 4; + + //Constants + private static final int + /*!underlined*/ v = 24; + + //Constants + private static final int + /*blink*/ e = 5; + + //Constants + private static final int + /*steady*/ n = 25; + + //Constants + private static final int + /*hide*/ h = 8; + + //Constants + private static final int + /*all out*/ a = 0; + private int[] colortranslation; //translation table + private int leng; + + private Colorizer() { + colortranslation = new int[128]; + colortranslation[83] = S; + colortranslation[82] = R; + colortranslation[71] = G; + colortranslation[89] = Y; + colortranslation[66] = B; + colortranslation[77] = M; + colortranslation[67] = C; + colortranslation[87] = W; + colortranslation[115] = s; + colortranslation[114] = r; + colortranslation[103] = g; + colortranslation[121] = y; + colortranslation[98] = b; + colortranslation[109] = m; + colortranslation[99] = c; + colortranslation[119] = w; + colortranslation[102] = f; + colortranslation[100] = d; + colortranslation[105] = i; + colortranslation[106] = j; + colortranslation[117] = u; + colortranslation[118] = v; + colortranslation[101] = e; + colortranslation[110] = n; + colortranslation[104] = h; + colortranslation[97] = a; + Self = this; + } + + //constructor + public String colorize(String str, boolean support) { + StringBuffer out = new StringBuffer(str.length() + 20); + int parsecursor = 0; + int foundcursor = 0; + boolean done = false; + + while (!done) { + foundcursor = str.indexOf(ColorHelper.MARKER_CODE, parsecursor); + + if (foundcursor != -1) { + out.append(str.substring(parsecursor, foundcursor)); + + if (support) { + out.append(addEscapeSequence(str.substring(foundcursor + 1, + foundcursor + 2))); + } + + parsecursor = foundcursor + 2; + } else { + out.append(str.substring(parsecursor, str.length())); + done = true; + } + } + + /* + * This will always add a "reset all" escape sequence + * behind the input string. + * Basically this is a good idea, because developers tend to + * forget writing colored strings properly. + */ + if (support) { + out.append(addEscapeSequence("a")); + } + + return out.toString(); + } + + //colorize + private String addEscapeSequence(String attribute) { + StringBuffer tmpbuf = new StringBuffer(10); + byte[] tmpbytes = attribute.getBytes(); + int key = (int) tmpbytes[0]; + tmpbuf.append((char) 27); + tmpbuf.append((char) 91); + tmpbuf.append((new Integer(colortranslation[key])).toString()); + tmpbuf.append((char) 109); + + return tmpbuf.toString(); + } + + //addEscapeSequence + public static Colorizer getReference() { + if (Self != null) { + return (Colorizer) Self; + } else { + return new Colorizer(); + } + } + + //getReference + private static void announceResult(boolean res) { + if (res) { + System.out.println("[#" + testcount + "] ok."); + } else { + System.out.println("[#" + testcount + + "] failed (see possible StackTrace)."); + } + } + + //announceResult + private static void announceTest(String what) { + testcount++; + System.out.println("Test #" + testcount + " [" + what + "]:"); + } + + //announceTest + private static void bfcolorTest(String color) { + System.out.println("->" + + myColorizer.colorize(ColorHelper.boldcolorizeText("COLOR", color), + true) + "<-"); + } + + //bfcolorTest + private static void fcolorTest(String color) { + System.out.println("->" + + myColorizer.colorize(ColorHelper.colorizeText("COLOR", color), true) + + "<-"); + } + + //fcolorTest + private static void bcolorTest(String color) { + System.out.println("->" + + myColorizer.colorize(ColorHelper.colorizeBackground(" ", color), + true) + "<-"); + } + + //bcolorTest + public static void main(String[] args) { + try { + announceTest("Instantiation"); + myColorizer = Colorizer.getReference(); + announceResult(true); + announceTest("Textcolor Tests"); + fcolorTest(ColorHelper.BLACK); + fcolorTest(ColorHelper.RED); + fcolorTest(ColorHelper.GREEN); + fcolorTest(ColorHelper.YELLOW); + fcolorTest(ColorHelper.BLUE); + fcolorTest(ColorHelper.MAGENTA); + fcolorTest(ColorHelper.CYAN); + fcolorTest(ColorHelper.white); + announceResult(true); + announceTest("Bold textcolor Tests"); + bfcolorTest(ColorHelper.BLACK); + bfcolorTest(ColorHelper.RED); + bfcolorTest(ColorHelper.GREEN); + bfcolorTest(ColorHelper.YELLOW); + bfcolorTest(ColorHelper.BLUE); + bfcolorTest(ColorHelper.MAGENTA); + bfcolorTest(ColorHelper.CYAN); + bfcolorTest(ColorHelper.white); + announceResult(true); + announceTest("Background Tests"); + bcolorTest(ColorHelper.BLACK); + bcolorTest(ColorHelper.RED); + bcolorTest(ColorHelper.GREEN); + bcolorTest(ColorHelper.YELLOW); + bcolorTest(ColorHelper.BLUE); + bcolorTest(ColorHelper.MAGENTA); + bcolorTest(ColorHelper.CYAN); + bcolorTest(ColorHelper.white); + announceResult(true); + announceTest("Mixed Color Tests"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.colorizeText("COLOR", + ColorHelper.white, ColorHelper.BLUE), true) + "<-"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.colorizeText("COLOR", + ColorHelper.YELLOW, ColorHelper.GREEN), true) + "<-"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.boldcolorizeText("COLOR", + ColorHelper.white, ColorHelper.BLUE), true) + "<-"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.boldcolorizeText("COLOR", + ColorHelper.YELLOW, ColorHelper.GREEN), true) + "<-"); + announceResult(true); + announceTest("Style Tests"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.boldText("Bold"), true) + + "<-"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.italicText("Italic"), true) + + "<-"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.underlinedText("Underlined"), + true) + "<-"); + System.out.println("->" + + myColorizer.colorize(ColorHelper.blinkingText("Blinking"), true) + + "<-"); + announceResult(true); + announceTest("Visible length test"); + + String colorized = ColorHelper.boldcolorizeText("STRING", + ColorHelper.YELLOW); + System.out.println("->" + myColorizer.colorize(colorized, true) + + "<-"); + System.out.println("Visible length=" + + ColorHelper.getVisibleLength(colorized)); + colorized = ColorHelper.boldcolorizeText("BANNER", + ColorHelper.white, ColorHelper.BLUE) + + ColorHelper.colorizeText("COLOR", ColorHelper.white, + ColorHelper.BLUE) + ColorHelper.underlinedText("UNDER"); + System.out.println("->" + myColorizer.colorize(colorized, true) + + "<-"); + System.out.println("Visible length=" + + ColorHelper.getVisibleLength(colorized)); + announceResult(true); + + if (false) { + throw new Exception(); //this will shut up jikes + } + } catch (Exception e) { + announceResult(false); + e.printStackTrace(); + } + } + + //main (test routine) +} + + +//class Colorizer diff --git a/src/com/sshtools/daemon/terminal/Editline.java b/src/com/sshtools/daemon/terminal/Editline.java new file mode 100644 index 0000000000000000000000000000000000000000..5f17dcd4ca31d70eb2c338f02fb39283ad1afd14 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/Editline.java @@ -0,0 +1,503 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class Editline { + //Aggregations (inner class!) + private Buffer buf; + + //Members + private TerminalIO myIO; + private int Cursor = 0; + private boolean InsertMode = true; + private int lastSize = 0; + private boolean hardwrapped = false; + private char lastread; + private int lastcurspos = 0; + private boolean maskInput = false; + private char mask = '*'; + + /** + * Creates a new Editline object. + * + * @param io + */ + public Editline(TerminalIO io) { + myIO = io; + + //allways full length + buf = new Buffer(myIO.getColumns() - 1); + Cursor = 0; + InsertMode = true; + } + + //constructor + public int size() { + return buf.size(); + } + + //getSize + public String getValue() { + return buf.toString(); + } + + //getValue + public void setValue(String str) + throws BufferOverflowException, IOException { + storeSize(); + + //buffer + buf.clear(); + + //cursor + Cursor = 0; + + //screen + myIO.moveLeft(lastSize); + myIO.eraseToEndOfLine(); + append(str); + } + + //setValue + public void maskInput(boolean maskInput) { + this.maskInput = maskInput; + } + + /** + * + * + * @param mask + */ + public void setMask(char mask) { + this.mask = mask; + } + + /** + * + * + * @throws IOException + */ + public void clear() throws IOException { + storeSize(); + + //Buffer + buf.clear(); + + //Cursor + Cursor = 0; + + //Screen + draw(); + } + + //clear + public String getSoftwrap() throws IndexOutOfBoundsException, IOException { + //Wrap from Buffer + String content = buf.toString(); + int idx = content.lastIndexOf(" "); + + if (idx == -1) { + content = ""; + } else { + //System.out.println("Line:softwrap:lastspace:"+idx); + content = content.substring(idx + 1, content.length()); + + //System.out.println("Line:softwrap:wraplength:"+content.length()); + //Cursor + //remeber relative cursor pos + Cursor = size(); + Cursor = Cursor - content.length(); + + //buffer + for (int i = 0; i < content.length(); i++) { + buf.removeCharAt(Cursor); + } + + //screen + myIO.moveLeft(content.length()); + myIO.eraseToEndOfLine(); + + //System.out.println("Line:softwrap:buffercontent:"+buf.toString()); + } + + return content + getLastRead(); + } + + //getSoftWrap + public String getHardwrap() throws IndexOutOfBoundsException, IOException { + //Buffer + String content = buf.toString(); + content = content.substring(Cursor, content.length()); + + //System.out.println("buffer:tostring:"+buf.toString()+":"); + //System.out.println("buffer:size:"+buf.size()); + int lastsize = buf.size(); + + for (int i = Cursor; i < lastsize; i++) { + buf.removeCharAt(Cursor); + + //System.out.println("buffer:removing char #"+i); + } + + //System.out.println("buffer:tostring:"+buf.toString()+":"); + //cursor stays + //screen + myIO.eraseToEndOfLine(); + + return content; + } + + //getHardWrap + private void setCharAt(int pos, char ch) + throws IndexOutOfBoundsException, IOException { + //buffer + buf.setCharAt(pos, ch); + + //cursor + //implements overwrite mode no change + //screen + draw(); + } + + //setCharAt + private void insertCharAt(int pos, char ch) + throws BufferOverflowException, IndexOutOfBoundsException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(1); + buf.insertCharAt(pos, ch); + + //cursor adjustment (so that it stays in "same" pos) + if (Cursor >= pos) { + Cursor++; + } + + //screen + draw(); + } + + //insertCharAt + private void removeCharAt(int pos) + throws IndexOutOfBoundsException, IOException { + storeSize(); + + //buffer + buf.removeCharAt(pos); + + //cursor + if (Cursor > pos) { + Cursor--; + } + + //screen + draw(); + } + + //removeChatAt + private void insertStringAt(int pos, String str) + throws BufferOverflowException, IndexOutOfBoundsException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(str.length()); + + for (int i = 0; i < str.length(); i++) { + buf.insertCharAt(pos, str.charAt(i)); + + //Cursor + Cursor++; + } + + //screen + draw(); + } + + //insertStringAt + public void append(char ch) throws BufferOverflowException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(1); + buf.append(ch); + + //cursor + Cursor++; + + //screen + if (!maskInput) { + myIO.write(ch); + } else { + myIO.write(mask); + } + } + + //append(char) + public void append(String str) throws BufferOverflowException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(str.length()); + + for (int i = 0; i < str.length(); i++) { + buf.append(str.charAt(i)); + + //Cursor + Cursor++; + } + + //screen + if (!maskInput) { + myIO.write(str); + } else { + for (int i = 0; i < str.length(); i++) { + myIO.write(mask); + } + } + } + + //append(String) + public int getCursorPosition() { + return Cursor; + } + + //getCursorPosition + public void setCursorPosition(int pos) { + if (buf.size() < pos) { + Cursor = buf.size(); + } else { + Cursor = pos; + } + + //System.out.println("Editline:cursor:"+Cursor); + } + + //setCursorPosition + private char getLastRead() { + return lastread; + } + + //getLastRead + private void setLastRead(char ch) { + lastread = ch; + } + + //setLastRead + public boolean isInInsertMode() { + return InsertMode; + } + + //isInInsertMode + public void setInsertMode(boolean b) { + InsertMode = b; + } + + //setInsertMode + public boolean isHardwrapped() { + return hardwrapped; + } + + //isHardwrapped + public void setHardwrapped(boolean b) { + hardwrapped = b; + } + + //setHardwrapped + public int run() { + try { + int in = 0; + + do { + //get next key + in = myIO.read(); + + //store cursorpos + lastcurspos = Cursor; + + switch (in) { + case TerminalIO.LEFT: + + if (!moveLeft()) { + return in; + } + + break; + + case TerminalIO.RIGHT: + + if (!moveRight()) { + return in; + } + + break; + + case TerminalIO.BACKSPACE: + + try { + if (Cursor == 0) { + return in; + } else { + removeCharAt(Cursor - 1); + } + } catch (IndexOutOfBoundsException ioobex) { + myIO.bell(); + } + + break; + + case TerminalIO.DELETE: + + try { + removeCharAt(Cursor); + } catch (IndexOutOfBoundsException ioobex) { + myIO.bell(); + } + + break; + + case TerminalIO.ENTER: + case TerminalIO.UP: + case TerminalIO.DOWN: + case TerminalIO.TABULATOR: + return in; + + default: + + try { + handleCharInput(in); + } catch (BufferOverflowException boex) { + setLastRead((char) in); + + return in; + } + } + + myIO.flush(); + } while (true); + } catch (IOException ioe) { + return TerminalIO.IOERROR; + } + } + + //run + public void draw() throws IOException { + myIO.moveLeft(lastcurspos); + myIO.eraseToEndOfLine(); + + if (!maskInput) { + myIO.write(buf.toString()); + } else { + for (int i = 0; i < buf.size(); i++) { + myIO.write(mask); + } + } + + //adjust screen cursor hmm + if (Cursor < buf.size()) { + myIO.moveLeft(buf.size() - Cursor); + } + } + + private boolean moveRight() throws IOException { + //cursor + if (Cursor < buf.size()) { + Cursor++; + + //screen + myIO.moveRight(1); + + return true; + } else { + return false; + } + } + + private boolean moveLeft() throws IOException { + //cursor + if (Cursor > 0) { + Cursor--; + + //screen + myIO.moveLeft(1); + + return true; + } else { + return false; + } + } + + private boolean isCursorAtEnd() { + return (Cursor == buf.size()); + } + + private void handleCharInput(int ch) + throws BufferOverflowException, IOException { + if (isCursorAtEnd()) { + append((char) ch); + } else { + if (isInInsertMode()) { + try { + insertCharAt(Cursor, (char) ch); + } catch (BufferOverflowException ex) { + //ignore buffer overflow on insert + myIO.bell(); + } + } else { + setCharAt(Cursor, (char) ch); + } + } + } + + private void storeSize() { + lastSize = buf.size(); + } + + class Buffer extends CharBuffer { + public Buffer(int size) { + super(size); + } + } +} diff --git a/src/com/sshtools/daemon/terminal/Terminal.java b/src/com/sshtools/daemon/terminal/Terminal.java new file mode 100644 index 0000000000000000000000000000000000000000..6314e2adf6d0a7eb57abe2e696ac1ef0c1b226b5 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/Terminal.java @@ -0,0 +1,230 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public interface Terminal { + //Constants + + /** */ + public static final byte EOT = 4; + + /** */ + public static final byte BS = 8; + + /** */ + public static final byte DEL = 127; + + /** */ + public static final byte HT = 9; + + /** */ + public static final byte FF = 12; + + /** */ + public static final byte SGR = 1; + + /** */ + public static final byte CAN = 24; + + /** */ + public static final byte ESC = 27; + + /** */ + public static final byte LSB = 91; + + /** */ + public static final byte SEMICOLON = 59; + + /** */ + public static final byte A = 65; + + /** */ + public static final byte B = 66; + + /** */ + public static final byte C = 67; + + /** */ + public static final byte D = 68; + + /** */ + public static final byte E = 69; // for next Line (like CR/LF) + + /** */ + public static final byte H = 72; // for Home and Positionsetting or f + + /** */ + public static final byte f = 102; + + /** */ + public static final byte r = 114; + + /** */ + public static final byte LE = 75; // K...line erase actions related + + /** */ + public static final byte SE = 74; // J...screen erase actions related + + /** + * + * + * @return + */ + public String getName(); + + /** + * + * + * @param byteread + * + * @return + */ + public int translateControlCharacter(int byteread); + + /** + * + * + * @param buffer + * + * @return + */ + public int translateEscapeSequence(int[] buffer); + + /** + * + * + * @param eraseFunc + * + * @return + */ + public byte[] getEraseSequence(int eraseFunc); + + /** + * + * + * @param dir + * @param times + * + * @return + */ + public byte[] getCursorMoveSequence(int dir, int times); + + /** + * + * + * @param pos + * + * @return + */ + public byte[] getCursorPositioningSequence(int[] pos); + + /** + * + * + * @param sequence + * + * @return + */ + public byte[] getSpecialSequence(int sequence); + + /** + * + * + * @param topmargin + * @param bottommargin + * + * @return + */ + public byte[] getScrollMarginsSequence(int topmargin, int bottommargin); + + /** + * + * + * @param type + * @param param + * + * @return + */ + public byte[] getGRSequence(int type, int param); + + /** + * + * + * @param str + * + * @return + */ + public String format(String str); + + /** + * + * + * @return + */ + public byte[] getInitSequence(); + + /** + * + * + * @return + */ + public boolean supportsSGR(); + + /** + * + * + * @return + */ + public boolean supportsScrolling(); + + /** + * + * + * @return + */ + public int getAtomicSequenceLength(); +} + + +//interface Terminal diff --git a/src/com/sshtools/daemon/terminal/TerminalFactory.java b/src/com/sshtools/daemon/terminal/TerminalFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..1688959f3337996e53f637715c7e4baf7361b7b1 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/TerminalFactory.java @@ -0,0 +1,69 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class TerminalFactory { + /** + * Creates a new TerminalFactory object. + */ + public TerminalFactory() { + } + + /** + * + * + * @param term + * + * @return + */ + public static Terminal newInstance(String term) { + if (term.equalsIgnoreCase("ANSI")) { + return new ansi(); + } else if (term.equalsIgnoreCase("xterm")) { + return new xterm(); + } else { + return new vt100(); + } + } +} diff --git a/src/com/sshtools/daemon/terminal/TerminalIO.java b/src/com/sshtools/daemon/terminal/TerminalIO.java new file mode 100644 index 0000000000000000000000000000000000000000..7f3746e5b157f26dbc98b1e057dbf85f6d5d3bf5 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/TerminalIO.java @@ -0,0 +1,1133 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.session.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class TerminalIO implements PseudoTerminal { + // implements BasicTerminalIO { + + /** */ + public static final int EOL_CRLF = 1; + + /** */ + public static final int EOL_CR = 2; + + /** */ + public static final int[] HOME = { 0, 0 }; + + /** */ + public static final int IOERROR = -1; //CTRL-D beim login + + /** */ + public static final int + // Positioning 10xx + UP = 1001; //CTRL-D beim login + + /** */ + public static final int DOWN = 1002; //CTRL-D beim login + + /** */ + public static final int RIGHT = 1003; //CTRL-D beim login + + /** */ + public static final int LEFT = 1004; //CTRL-D beim login + + /** */ + public static final int STORECURSOR = 1051; //CTRL-D beim login + + /** */ + public static final int RESTORECURSOR = 1052; //CTRL-D beim login + + /** */ + public static final int + // Erasing 11xx + EEOL = 1100; //CTRL-D beim login + + /** */ + public static final int EBOL = 1101; //CTRL-D beim login + + /** */ + public static final int EEL = 1103; //CTRL-D beim login + + /** */ + public static final int EEOS = 1104; //CTRL-D beim login + + /** */ + public static final int EBOS = 1105; //CTRL-D beim login + + /** */ + public static final int EES = 1106; //CTRL-D beim login + + /** */ + public static final int + // Escape Sequence-ing 12xx + ESCAPE = 1200; //CTRL-D beim login + + /** */ + public static final int BYTEMISSING = 1201; //CTRL-D beim login + + /** */ + public static final int UNRECOGNIZED = 1202; //CTRL-D beim login + + /** */ + public static final int + // Control Characters 13xx + ENTER = 10; //CTRL-D beim login + + /** */ + public static final int + //ENTER = 1300, //LF is ENTER at the moment + TABULATOR = 1301; //CTRL-D beim login + + /** */ + public static final int DELETE = 1302; //CTRL-D beim login + + /** */ + public static final int BACKSPACE = 1303; //CTRL-D beim login + + /** */ + public static final int COLORINIT = 1304; //CTRL-D beim login + + /** */ + public static final int HANDLED = 1305; //CTRL-D beim login + + /** */ + public static final int LOGOUTREQUEST = 1306; //CTRL-D beim login + + /** */ + public static final int LineUpdate = 475; + + /** */ + public static final int CharacterUpdate = 476; + + /** */ + public static final int ScreenpartUpdate = 477; + + /** */ + public static final int EditBuffer = 575; + + /** */ + public static final int LineEditBuffer = 576; + + /** */ + public static final int BEL = 7; + + /** */ + public static final int BS = 8; + + /** */ + public static final int DEL = 127; + + /** */ + public static final int CR = 13; + + /** */ + public static final int LF = 10; + + /** */ + public static final int FCOLOR = 10001; + + /** */ + public static final int BCOLOR = 10002; + + /** */ + public static final int STYLE = 10003; + + /** */ + public static final int RESET = 10004; + + /** */ + public static final int BOLD = 1; + + /** */ + public static final int BOLD_OFF = 22; + + /** */ + public static final int ITALIC = 3; + + /** */ + public static final int ITALIC_OFF = 23; + + /** */ + public static final int BLINK = 5; + + /** */ + public static final int BLINK_OFF = 25; + + /** */ + public static final int UNDERLINED = 4; + + /** */ + public static final int UNDERLINED_OFF = 24; + + //Constants + + /** */ + public static final int BLACK = 30; + + /** */ + public static final int RED = 31; + + /** */ + public static final int GREEN = 32; + + /** */ + public static final int YELLOW = 33; + + /** */ + public static final int BLUE = 34; + + /** */ + public static final int MAGENTA = 35; + + /** */ + public static final int CYAN = 36; + + /** */ + public static final int white = 37; + + /** */ + public static final String CRLF = "\r\n"; + private Terminal terminal; + private DataInputStream in; + private DataOutputStream out; + private boolean closing; + private boolean cr; + private boolean nl; + private boolean acousticSignalling; //flag for accoustic signalling + private boolean autoflush; //flag for autoflushing mode + private int eol = EOL_CRLF; + private int lastByte; + private boolean uselast = false; + private Colorizer color = Colorizer.getReference(); + private String term; + private int cols; + private int rows; + private PipedInputStream masterIn; + private PipedOutputStream masterOut; + private InputStream slaveIn; + private OutputStream slaveOut; + private IOStreamConnector ios; + + //private OutputStream masterOut; + // private OutputStream slaveOut = new SlaveOutputStream(); + public TerminalIO(InputStream in, OutputStream out, String term, int cols, + int rows) throws IOException { + attachStreams(in, out); + this.term = term; + this.rows = rows; + this.cols = cols; + acousticSignalling = true; + masterOut = new PipedOutputStream(); + masterIn = new PipedInputStream(masterOut); + autoflush = true; + closing = false; + cr = false; + + //set default terminal + setDefaultTerminal(); + } + + /** + * + * + * @return + */ + public InputStream getMasterInputStream() { + return masterIn; + } + + /** + * + * + * @param slaveIn + */ + public void bindSlaveInputStream(InputStream slaveIn) { + this.slaveIn = slaveIn; + this.ios = new IOStreamConnector(slaveIn, masterOut); + } + + /** + * + * + * @param slaveOut + */ + public void bindSlaveOutputStream(OutputStream slaveOut) { + this.slaveOut = slaveOut; + } + + /** + * + * + * @return + */ + public OutputStream getSlaveOutputStream() { + return slaveOut; + } + + /** + * + * + * @return + */ + public int getWidth() { + return 0; + } + + /** + * + * + * @return + */ + public int getHeight() { + return 0; + } + + /** + * + * + * @return + */ + public String getTerm() { + return terminal.getName(); + } + + /** + * + * + * @return + */ + public String getEncodedTerminalModes() { + return ""; + } + + /* public void setMasterOutputStream(OutputStream masterOut) { + this.masterOut = masterOut; + }*/ + public InputStream getAttachedInputStream() throws IOException { + if (in == null) { + throw new IOException( + "The teminal is not attached to an InputStream"); + } + + return in; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public OutputStream getAttachedOutputStream() throws IOException { + if (out == null) { + throw new IOException( + "The terminal is not attached to an OutputStream"); + } + + return out; + } + + /** + * + */ + public void detachStreams() { + this.in = null; + this.out = null; + } + + /** + * + * + * @return + */ + public int getEOL() { + return eol; + } + + /** + * + * + * @return + */ + public String getEOLString() { + return ((eol == EOL_CR) ? "\r" : "\r\n"); + } + + /** + * + * + * @param eol + */ + public void setEOL(int eol) { + this.eol = eol; + } + + /** + * + * + * @param in + * @param out + */ + public void attachStreams(InputStream in, OutputStream out) { + this.in = new DataInputStream(new BufferedInputStream(in)); + this.out = new DataOutputStream(new BufferedOutputStream(out)); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public int read() throws IOException { + int i = stripCRSeq(rawread()); + + //translate possible control sequences + i = terminal.translateControlCharacter(i); + + if ((i > 256) && (i == ESCAPE)) { + i = handleEscapeSequence(i); + } + + return i; + } + + /** + * + * + * @param ch + * + * @throws IOException + */ + public void write(char ch) throws IOException { + write((byte) ch); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @param str + * + * @throws IOException + */ + public void write(String str) throws IOException { + write((color.colorize(str, terminal.supportsSGR())).getBytes()); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @param str + * + * @throws IOException + */ + public void println(String str) throws IOException { + write(str); + write(getEOLString().getBytes()); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @throws IOException + */ + public void println() throws IOException { + write(getEOLString().getBytes()); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @throws IOException + */ + public void eraseToEndOfLine() throws IOException { + doErase(EEOL); + } + + /** + * + * + * @throws IOException + */ + public void eraseToBeginOfLine() throws IOException { + doErase(EBOL); + } + + /** + * + * + * @throws IOException + */ + public void eraseLine() throws IOException { + doErase(EEL); + } + + /** + * + * + * @throws IOException + */ + public void eraseToEndOfScreen() throws IOException { + doErase(EEOS); + } + + /** + * + * + * @throws IOException + */ + public void eraseToBeginOfScreen() throws IOException { + doErase(EBOS); + } + + /** + * + * + * @throws IOException + */ + public void eraseScreen() throws IOException { + doErase(EES); + } + + private void doErase(int funcConst) throws IOException { + write(terminal.getEraseSequence(funcConst)); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @param direction + * @param times + * + * @throws IOException + */ + public void moveCursor(int direction, int times) throws IOException { + write(terminal.getCursorMoveSequence(direction, times)); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @param times + * + * @throws IOException + */ + public void moveLeft(int times) throws IOException { + moveCursor(LEFT, times); + } + + /** + * + * + * @param times + * + * @throws IOException + */ + public void moveRight(int times) throws IOException { + moveCursor(RIGHT, times); + } + + /** + * + * + * @param times + * + * @throws IOException + */ + public void moveUp(int times) throws IOException { + moveCursor(UP, times); + } + + /** + * + * + * @param times + * + * @throws IOException + */ + public void moveDown(int times) throws IOException { + moveCursor(DOWN, times); + } + + /** + * + * + * @param row + * @param col + * + * @throws IOException + */ + public void setCursor(int row, int col) throws IOException { + int[] pos = new int[2]; + pos[0] = row; + pos[1] = col; + write(terminal.getCursorPositioningSequence(pos)); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @throws IOException + */ + public void homeCursor() throws IOException { + write(terminal.getCursorPositioningSequence(HOME)); + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @throws IOException + */ + public void storeCursor() throws IOException { + write(terminal.getSpecialSequence(STORECURSOR)); + } + + /** + * + * + * @throws IOException + */ + public void restoreCursor() throws IOException { + write(terminal.getSpecialSequence(RESTORECURSOR)); + } + + /** + * + * + * @throws IOException + */ + public void closeInput() throws IOException { + if (in == null) { + throw new IOException( + "The terminal is not attached to an inputstream"); + } + + in.close(); + } + + private int read16int() throws IOException { + if (in == null) { + throw new IOException( + "The terminal is not attached to an inputstream"); + } + + return in.readUnsignedShort(); + } + + private int rawread() throws IOException { + if (in == null) { + throw new IOException( + "The terminal is not attached to an inputstream"); + } + + return in.readUnsignedByte(); + } + + private int stripCRSeq(int input) throws IOException { + if (in == null) { + throw new IOException( + "The terminal is not attached to an inputstream"); + } + + // Check for CR + if (input == CR) { + // Mark the current position and test for LF + in.mark(1); + + // If there is more data to read, check for LF + if (in.available() > 0) { + int next = in.readUnsignedByte(); + + // If we don't have LF then reset back to the mark position + if (next != LF) { + in.reset(); + } + } + + return TerminalIO.ENTER; + } + + return input; + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void write(byte b) throws IOException { + if (out == null) { + throw new IOException( + "The terminal is not attached to an outputstream"); + } + + if (eol == EOL_CRLF) { + if (!cr && (b == 10)) { + out.write(13); + + //if(masterOut!=null) + // masterOut.write(13); + } + + //ensure CRLF(\r\n) is written for CR(\r) to adhere + //to the telnet protocol. + if (cr && (b != 10)) { + out.write(10); + + // if(masterOut!=null) + // masterOut.write(10); + } + + out.write(b); + + // if(masterOut!=null) + // masterOut.write(b); + if (b == 13) { + cr = true; + } else { + cr = false; + } + } else { + out.write(b); + + // if(masterOut!=null) + // masterOut.write(b); + } + } + + /** + * + * + * @param i + * + * @throws IOException + */ + public void write(int i) throws IOException { + write((byte) i); + } + + /** + * + * + * @param sequence + * + * @throws IOException + */ + public void write(byte[] sequence) throws IOException { + for (int z = 0; z < sequence.length; z++) { + write(sequence[z]); + } + } + + /** + * + * + * @param sequence + * + * @throws IOException + */ + public void write(int[] sequence) throws IOException { + for (int j = 0; j < sequence.length; j++) { + write((byte) sequence[j]); + } + } + + /** + * + * + * @throws IOException + */ + public void flush() throws IOException { + if (out == null) { + throw new IOException( + "The terminal is not attached to an outputstream"); + } + + // If were attached then flush, else ignore + out.flush(); + } + + /** + * + * + * @throws IOException + */ + public void closeOutput() throws IOException { + if (out == null) { + throw new IOException( + "The terminal is not attached to an outputstream"); + } + + closing = true; + out.close(); + } + + /** + * + * + * @param bool + */ + public void setSignalling(boolean bool) { + acousticSignalling = bool; + } + + /** + * + * + * @return + */ + public boolean isSignalling() { + return acousticSignalling; + } + + /** + * + * + * @throws IOException + */ + public void bell() throws IOException { + if (acousticSignalling) { + write(BEL); + } + + if (autoflush) { + flush(); + } + } + + /** + * + * + * @param topmargin + * @param bottommargin + * + * @return + * + * @throws IOException + */ + public boolean defineScrollRegion(int topmargin, int bottommargin) + throws IOException { + if (terminal.supportsScrolling()) { + write(terminal.getScrollMarginsSequence(topmargin, bottommargin)); + flush(); + + return true; + } else { + return false; + } + } + + /** + * + * + * @param color + * + * @throws IOException + */ + public void setForegroundColor(int color) throws IOException { + if (terminal.supportsSGR()) { + write(terminal.getGRSequence(FCOLOR, color)); + + if (autoflush) { + flush(); + } + } + } + + /** + * + * + * @param color + * + * @throws IOException + */ + public void setBackgroundColor(int color) throws IOException { + if (terminal.supportsSGR()) { + //this method adds the offset to the fg color by itself + write(terminal.getGRSequence(BCOLOR, color + 10)); + + if (autoflush) { + flush(); + } + } + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void setBold(boolean b) throws IOException { + if (terminal.supportsSGR()) { + if (b) { + write(terminal.getGRSequence(STYLE, BOLD)); + } else { + write(terminal.getGRSequence(STYLE, BOLD_OFF)); + } + + if (autoflush) { + flush(); + } + } + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void setUnderlined(boolean b) throws IOException { + if (terminal.supportsSGR()) { + if (b) { + write(terminal.getGRSequence(STYLE, UNDERLINED)); + } else { + write(terminal.getGRSequence(STYLE, UNDERLINED_OFF)); + } + + if (autoflush) { + flush(); + } + } + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void setItalic(boolean b) throws IOException { + if (terminal.supportsSGR()) { + if (b) { + write(terminal.getGRSequence(STYLE, ITALIC)); + } else { + write(terminal.getGRSequence(STYLE, ITALIC_OFF)); + } + + if (autoflush) { + flush(); + } + } + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void setBlink(boolean b) throws IOException { + if (terminal.supportsSGR()) { + if (b) { + write(terminal.getGRSequence(STYLE, BLINK)); + } else { + write(terminal.getGRSequence(STYLE, BLINK_OFF)); + } + + if (autoflush) { + flush(); + } + } + } + + /** + * + * + * @throws IOException + */ + public void resetAttributes() throws IOException { + if (terminal.supportsSGR()) { + write(terminal.getGRSequence(RESET, 0)); + } + } + + private int handleEscapeSequence(int i) throws IOException { + if (i == ESCAPE) { + int[] bytebuf = new int[terminal.getAtomicSequenceLength()]; + + //fill atomic length + //FIXME: ensure CAN, broken Escapes etc. + for (int m = 0; m < bytebuf.length; m++) { + bytebuf[m] = read(); + } + + return terminal.translateEscapeSequence(bytebuf); + } + + if (i == BYTEMISSING) { + //FIXME:longer escapes etc... + } + + return HANDLED; + } + + /** + * + * + * @return + */ + public boolean isAutoflushing() { + return autoflush; + } + + /** + * + * + * @param b + */ + public void setAutoflushing(boolean b) { + autoflush = b; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + closeOutput(); + } + + /** + * + * + * @return + */ + public Terminal getTerminal() { + return terminal; + } + + //getTerminal + public void setDefaultTerminal() throws IOException { + //set the terminal passing the negotiated string + setTerminal(term); + } + + /** + * + * + * @param terminalName + * + * @throws IOException + */ + public void setTerminal(String terminalName) throws IOException { + terminal = TerminalFactory.newInstance(terminalName); + + //Terminal is set we init it.... + initTerminal(); + } + + private void initTerminal() throws IOException { + write(terminal.getInitSequence()); + flush(); + } + + /** + * + * + * @return + */ + public int getRows() { + return rows; + } + + /** + * + * + * @return + */ + public int getColumns() { + return cols; + } +} + + +//class TerminalIO diff --git a/src/com/sshtools/daemon/terminal/UserInput.java b/src/com/sshtools/daemon/terminal/UserInput.java new file mode 100644 index 0000000000000000000000000000000000000000..6f446a5b716f1d776d3b1ab23a9cbead37cd121e --- /dev/null +++ b/src/com/sshtools/daemon/terminal/UserInput.java @@ -0,0 +1,510 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + +import java.io.IOException; +import java.io.OutputStream; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class UserInput implements Runnable { + //Aggregations (inner class!) + private Buffer buf; + + //Members + private TerminalIO myIO; + private int Cursor = 0; + private boolean InsertMode = true; + private int lastSize = 0; + private boolean hardwrapped = false; + private char lastread; + private int lastcurspos = 0; + private boolean maskInput = false; + private char mask = '*'; + private OutputStream pout; + + /** + * Creates a new UserInput object. + * + * @param io + * @param pout + */ + public UserInput(TerminalIO io, OutputStream pout) { + myIO = io; + this.pout = pout; + + //allways full length + buf = new Buffer(myIO.getColumns() - 1); + Cursor = 0; + InsertMode = true; + + Thread thread = new Thread(this); + thread.setDaemon(true); + thread.start(); + } + + //constructor + public int size() { + return buf.size(); + } + + //getSize + public String getValue() { + return buf.toString(); + } + + //getValue + public void setValue(String str) + throws BufferOverflowException, IOException { + storeSize(); + + //buffer + buf.clear(); + + //cursor + Cursor = 0; + + //screen + myIO.moveLeft(lastSize); + myIO.eraseToEndOfLine(); + append(str); + } + + //setValue + public void maskInput(boolean maskInput) { + this.maskInput = maskInput; + } + + /** + * + * + * @param mask + */ + public void setMask(char mask) { + this.mask = mask; + } + + /** + * + * + * @throws IOException + */ + public void clear() throws IOException { + storeSize(); + + //Buffer + buf.clear(); + + //Cursor + Cursor = 0; + + //Screen + draw(); + } + + //clear + public String getSoftwrap() throws IndexOutOfBoundsException, IOException { + //Wrap from Buffer + String content = buf.toString(); + int idx = content.lastIndexOf(" "); + + if (idx == -1) { + content = ""; + } else { + //System.out.println("Line:softwrap:lastspace:"+idx); + content = content.substring(idx + 1, content.length()); + + //System.out.println("Line:softwrap:wraplength:"+content.length()); + //Cursor + //remeber relative cursor pos + Cursor = size(); + Cursor = Cursor - content.length(); + + //buffer + for (int i = 0; i < content.length(); i++) { + buf.removeCharAt(Cursor); + } + + //screen + myIO.moveLeft(content.length()); + myIO.eraseToEndOfLine(); + + //System.out.println("Line:softwrap:buffercontent:"+buf.toString()); + } + + return content + getLastRead(); + } + + //getSoftWrap + public String getHardwrap() throws IndexOutOfBoundsException, IOException { + //Buffer + String content = buf.toString(); + content = content.substring(Cursor, content.length()); + + //System.out.println("buffer:tostring:"+buf.toString()+":"); + //System.out.println("buffer:size:"+buf.size()); + int lastsize = buf.size(); + + for (int i = Cursor; i < lastsize; i++) { + buf.removeCharAt(Cursor); + + //System.out.println("buffer:removing char #"+i); + } + + //System.out.println("buffer:tostring:"+buf.toString()+":"); + //cursor stays + //screen + myIO.eraseToEndOfLine(); + + return content; + } + + //getHardWrap + private void setCharAt(int pos, char ch) + throws IndexOutOfBoundsException, IOException { + //buffer + buf.setCharAt(pos, ch); + + //cursor + //implements overwrite mode no change + //screen + draw(); + } + + //setCharAt + private void insertCharAt(int pos, char ch) + throws BufferOverflowException, IndexOutOfBoundsException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(1); + buf.insertCharAt(pos, ch); + + //cursor adjustment (so that it stays in "same" pos) + if (Cursor >= pos) { + Cursor++; + } + + //screen + draw(); + } + + //insertCharAt + private void removeCharAt(int pos) + throws IndexOutOfBoundsException, IOException { + storeSize(); + + //buffer + buf.removeCharAt(pos); + + //cursor + if (Cursor > pos) { + Cursor--; + } + + //screen + draw(); + } + + //removeChatAt + private void insertStringAt(int pos, String str) + throws BufferOverflowException, IndexOutOfBoundsException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(str.length()); + + for (int i = 0; i < str.length(); i++) { + buf.insertCharAt(pos, str.charAt(i)); + + //Cursor + Cursor++; + } + + //screen + draw(); + } + + //insertStringAt + public void append(char ch) throws BufferOverflowException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(1); + buf.append(ch); + + //cursor + Cursor++; + + //screen + if (!maskInput) { + myIO.write(ch); + } else { + myIO.write(mask); + } + } + + //append(char) + public void append(String str) throws BufferOverflowException, IOException { + storeSize(); + + //buffer + buf.ensureSpace(str.length()); + + for (int i = 0; i < str.length(); i++) { + buf.append(str.charAt(i)); + + //Cursor + Cursor++; + } + + //screen + if (!maskInput) { + myIO.write(str); + } else { + for (int i = 0; i < str.length(); i++) { + myIO.write(mask); + } + } + } + + //append(String) + public int getCursorPosition() { + return Cursor; + } + + //getCursorPosition + public void setCursorPosition(int pos) { + if (buf.size() < pos) { + Cursor = buf.size(); + } else { + Cursor = pos; + } + + //System.out.println("Editline:cursor:"+Cursor); + } + + //setCursorPosition + private char getLastRead() { + return lastread; + } + + //getLastRead + private void setLastRead(char ch) { + lastread = ch; + } + + //setLastRead + public boolean isInInsertMode() { + return InsertMode; + } + + //isInInsertMode + public void setInsertMode(boolean b) { + InsertMode = b; + } + + //setInsertMode + public boolean isHardwrapped() { + return hardwrapped; + } + + //isHardwrapped + public void setHardwrapped(boolean b) { + hardwrapped = b; + } + + //setHardwrapped + public void run() { + try { + int in = 0; + + do { + //get next key + in = myIO.read(); + + //store cursorpos + lastcurspos = Cursor; + + switch (in) { + case TerminalIO.LEFT: + moveLeft(); + + break; + + case TerminalIO.RIGHT: + moveRight(); + + break; + + case TerminalIO.BACKSPACE: + + try { + if (Cursor != 0) { + removeCharAt(Cursor - 1); + } + } catch (IndexOutOfBoundsException ioobex) { + myIO.bell(); + } + + break; + + case TerminalIO.DELETE: + + try { + removeCharAt(Cursor); + } catch (IndexOutOfBoundsException ioobex) { + myIO.bell(); + } + + break; + + case TerminalIO.ENTER: + myIO.write(myIO.getEOLString()); + + if (buf.size() > 0) { + pout.write(buf.toString().getBytes()); + } + + buf.clear(); + Cursor = 0; + pout.write(TerminalIO.CRLF.getBytes()); + + break; + + case TerminalIO.UP: + case TerminalIO.DOWN: + case TerminalIO.TABULATOR:default: + + try { + handleCharInput(in); + } catch (BufferOverflowException boex) { + setLastRead((char) in); + } + } + + myIO.flush(); + } while (true); + } catch (IOException ioe) { + return; + } + } + + //run + public void draw() throws IOException { + myIO.moveLeft(lastcurspos); + myIO.eraseToEndOfLine(); + + if (!maskInput) { + myIO.write(buf.toString()); + } else { + for (int i = 0; i < buf.size(); i++) { + myIO.write(mask); + } + } + + //adjust screen cursor hmm + if (Cursor < buf.size()) { + myIO.moveLeft(buf.size() - Cursor); + } + } + + private boolean moveRight() throws IOException { + //cursor + if (Cursor < buf.size()) { + Cursor++; + + //screen + myIO.moveRight(1); + + return true; + } else { + return false; + } + } + + private boolean moveLeft() throws IOException { + //cursor + if (Cursor > 0) { + Cursor--; + + //screen + myIO.moveLeft(1); + + return true; + } else { + return false; + } + } + + private boolean isCursorAtEnd() { + return (Cursor == buf.size()); + } + + private void handleCharInput(int ch) + throws BufferOverflowException, IOException { + if (isCursorAtEnd()) { + append((char) ch); + } else { + if (isInInsertMode()) { + try { + insertCharAt(Cursor, (char) ch); + } catch (BufferOverflowException ex) { + //ignore buffer overflow on insert + myIO.bell(); + } + } else { + setCharAt(Cursor, (char) ch); + } + } + } + + private void storeSize() { + lastSize = buf.size(); + } + + class Buffer extends CharBuffer { + public Buffer(int size) { + super(size); + } + } +} diff --git a/src/com/sshtools/daemon/terminal/ansi.java b/src/com/sshtools/daemon/terminal/ansi.java new file mode 100644 index 0000000000000000000000000000000000000000..22183b664010f975c7a7e3ca36451c0a5480aeda --- /dev/null +++ b/src/com/sshtools/daemon/terminal/ansi.java @@ -0,0 +1,68 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class ansi extends BasicTerminal { + /** + * + * + * @return + */ + public boolean supportsSGR() { + return true; + } + + //supportsSGR + public boolean supportsScrolling() { + return true; + } + + //supportsSoftScroll + public String getName() { + return "ansi"; + } +} + + +//class ansi diff --git a/src/com/sshtools/daemon/terminal/vt100.java b/src/com/sshtools/daemon/terminal/vt100.java new file mode 100644 index 0000000000000000000000000000000000000000..4900acbfd121965939372d287ae497873b77a7af --- /dev/null +++ b/src/com/sshtools/daemon/terminal/vt100.java @@ -0,0 +1,68 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class vt100 extends BasicTerminal { + /** + * + * + * @return + */ + public boolean supportsSGR() { + return false; + } + + //supportsSGR + public boolean supportsScrolling() { + return true; + } + + //supportsSoftScroll + public String getName() { + return "vt100"; + } +} + + +//class vt100 diff --git a/src/com/sshtools/daemon/terminal/xterm.java b/src/com/sshtools/daemon/terminal/xterm.java new file mode 100644 index 0000000000000000000000000000000000000000..adee18c5fe16879c2f5bf135f8159cc0e0584bc6 --- /dev/null +++ b/src/com/sshtools/daemon/terminal/xterm.java @@ -0,0 +1,68 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/** + * SSHTools - Java SSH API The contents of this package has been derived from + * the TelnetD library available from http://sourceforge.net/projects/telnetd + * The original license of the source code is as follows: TelnetD library + * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library + * is free software; you can either redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 2.1,1999 as + * published by the Free Software Foundation (see copy received along with the + * library), or under the terms of the BSD-style license received along with + * this library. + */ +package com.sshtools.daemon.terminal; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class xterm extends BasicTerminal { + /** + * + * + * @return + */ + public boolean supportsSGR() { + return true; + } + + //supportsSGR + public boolean supportsScrolling() { + return true; + } + + //supportsScrolling + public String getName() { + return "xterm"; + } +} + + +//class xterm diff --git a/src/com/sshtools/daemon/transport/TransportProtocolServer.java b/src/com/sshtools/daemon/transport/TransportProtocolServer.java new file mode 100644 index 0000000000000000000000000000000000000000..0c1fdb254799be73ec2d365adc2d93a6231455c0 --- /dev/null +++ b/src/com/sshtools/daemon/transport/TransportProtocolServer.java @@ -0,0 +1,448 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.transport; + +import com.sshtools.daemon.configuration.ServerConfiguration; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.transport.*; +import com.sshtools.j2ssh.transport.cipher.SshCipher; +import com.sshtools.j2ssh.transport.cipher.SshCipherFactory; +import com.sshtools.j2ssh.transport.hmac.SshHmac; +import com.sshtools.j2ssh.transport.hmac.SshHmacFactory; +import com.sshtools.j2ssh.transport.kex.KeyExchangeException; +import com.sshtools.j2ssh.transport.kex.SshKeyExchange; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class TransportProtocolServer extends TransportProtocolCommon { + private static Log log = LogFactory.getLog(TransportProtocolServer.class); + private Map acceptServices = new HashMap(); + private ServerConfiguration config; + private boolean refuse = false; + + /** + * Creates a new TransportProtocolServer object. + * + * @throws IOException + */ + public TransportProtocolServer() throws IOException { + config = (ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class); + } + + /** + * Creates a new TransportProtocolServer object. + * + * @param refuse + * + * @throws IOException + */ + public TransportProtocolServer(boolean refuse) throws IOException { + this(); + this.refuse = refuse; + } + + /** + * + */ + protected void onDisconnect() { + acceptServices.clear(); + } + + /** + * + * + * @param service + * + * @throws IOException + */ + public void acceptService(Service service) throws IOException { + acceptServices.put(service.getServiceName(), service); + } + + /** + * + * + * @throws IOException + */ + public void refuseConnection() throws IOException { + log.info("Refusing connection"); + + // disconnect with max_connections reason + sendDisconnect(SshMsgDisconnect.TOO_MANY_CONNECTIONS, + "Too many connections"); + } + + /** + * + * + * @throws MessageAlreadyRegisteredException + */ + public void registerTransportMessages() + throws MessageAlreadyRegisteredException { + messageStore.registerMessage(SshMsgServiceRequest.SSH_MSG_SERVICE_REQUEST, + SshMsgServiceRequest.class); + } + + /** + * + * + * @throws IOException + */ + protected void startBinaryPacketProtocol() throws IOException { + if (refuse) { + sendKeyExchangeInit(); + + //sshIn.open(); + refuseConnection(); + } else { + super.startBinaryPacketProtocol(); + } + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getDecryptionAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedCSEncryption(), + serverKexInit.getSupportedCSEncryption()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getEncryptionAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedSCEncryption(), + serverKexInit.getSupportedSCEncryption()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getInputStreamCompAlgortihm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedCSComp(), + serverKexInit.getSupportedCSComp()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getInputStreamMacAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedCSMac(), + serverKexInit.getSupportedCSMac()); + } + + /** + * + */ + protected void setLocalIdent() { + serverIdent = "SSH-" + PROTOCOL_VERSION + "-" + + SOFTWARE_VERSION_COMMENTS + " [SERVER]"; + } + + /** + * + * + * @return + */ + public String getLocalId() { + return serverIdent; + } + + /** + * + * + * @param msg + */ + protected void setLocalKexInit(SshMsgKexInit msg) { + log.debug(msg.toString()); + serverKexInit = msg; + } + + /** + * + * + * @return + */ + protected SshMsgKexInit getLocalKexInit() { + return serverKexInit; + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getOutputStreamCompAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedSCComp(), + serverKexInit.getSupportedSCComp()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getOutputStreamMacAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedSCMac(), + serverKexInit.getSupportedSCMac()); + } + + /** + * + * + * @param ident + */ + protected void setRemoteIdent(String ident) { + clientIdent = ident; + } + + /** + * + * + * @return + */ + public String getRemoteId() { + return clientIdent; + } + + /** + * + * + * @param msg + */ + protected void setRemoteKexInit(SshMsgKexInit msg) { + log.debug(msg.toString()); + clientKexInit = msg; + } + + /** + * + * + * @return + */ + protected SshMsgKexInit getRemoteKexInit() { + return clientKexInit; + } + + /** + * + * + * @return + * + * @throws IOException + * @throws TransportProtocolException + */ + protected SshMsgKexInit createLocalKexInit() throws IOException { + SshMsgKexInit msg = new SshMsgKexInit(properties); + Map keys = config.getServerHostKeys(); + + if (keys.size() > 0) { + Iterator it = keys.entrySet().iterator(); + List available = new ArrayList(); + + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + + if (SshKeyPairFactory.supportsKey(entry.getKey().toString())) { + available.add(entry.getKey()); + } else { + log.warn("Server host key algorithm '" + + entry.getKey().toString() + "' not supported"); + } + } + + if (available.size() > 0) { + msg.setSupportedPK(available); + } else { + throw new TransportProtocolException( + "No server host keys available"); + } + } else { + throw new TransportProtocolException( + "There are no server host keys available"); + } + + return msg; + } + + /** + * + * + * @throws IOException + */ + protected void onStartTransportProtocol() throws IOException { + } + + /** + * + * + * @param kex + * + * @throws IOException + * @throws KeyExchangeException + */ + protected void performKeyExchange(SshKeyExchange kex) + throws IOException { + // Determine the public key algorithm and obtain an instance + String keyType = determineAlgorithm(clientKexInit.getSupportedPublicKeys(), + serverKexInit.getSupportedPublicKeys()); + + // Create an instance of the public key from the factory + //SshKeyPair pair = SshKeyPairFactory.newInstance(keyType); + // Get the configuration and get the relevant host key + Map keys = config.getServerHostKeys(); + Iterator it = keys.entrySet().iterator(); + SshPrivateKey pk; //privateKeyFile = null; + + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + + if (entry.getKey().equals(keyType)) { + pk = (SshPrivateKey) entry.getValue(); + kex.performServerExchange(clientIdent, serverIdent, + clientKexInit.toByteArray(), serverKexInit.toByteArray(), pk); + + return; + } + } + + throw new KeyExchangeException( + "No host key available for the determined public key algorithm"); + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void onMessageReceived(SshMessage msg) throws IOException { + switch (msg.getMessageId()) { + case SshMsgServiceRequest.SSH_MSG_SERVICE_REQUEST: { + onMsgServiceRequest((SshMsgServiceRequest) msg); + + break; + } + } + } + + /** + * + * + * @param encryptCSKey + * @param encryptCSIV + * @param encryptSCKey + * @param encryptSCIV + * @param macCSKey + * @param macSCKey + * + * @throws AlgorithmNotAgreedException + * @throws AlgorithmOperationException + * @throws AlgorithmNotSupportedException + * @throws AlgorithmInitializationException + */ + protected void setupNewKeys(byte[] encryptCSKey, byte[] encryptCSIV, + byte[] encryptSCKey, byte[] encryptSCIV, byte[] macCSKey, + byte[] macSCKey) + throws AlgorithmNotAgreedException, AlgorithmOperationException, + AlgorithmNotSupportedException, AlgorithmInitializationException { + // Setup the encryption cipher + SshCipher sshCipher = SshCipherFactory.newInstance(getEncryptionAlgorithm()); + sshCipher.init(SshCipher.ENCRYPT_MODE, encryptSCIV, encryptSCKey); + algorithmsOut.setCipher(sshCipher); + + // Setup the decryption cipher + sshCipher = SshCipherFactory.newInstance(getDecryptionAlgorithm()); + sshCipher.init(SshCipher.DECRYPT_MODE, encryptCSIV, encryptCSKey); + algorithmsIn.setCipher(sshCipher); + + // Create and put our macs into operation + SshHmac hmac = SshHmacFactory.newInstance(getOutputStreamMacAlgorithm()); + hmac.init(macSCKey); + algorithmsOut.setHmac(hmac); + hmac = SshHmacFactory.newInstance(getInputStreamMacAlgorithm()); + hmac.init(macCSKey); + algorithmsIn.setHmac(hmac); + } + + private void onMsgServiceRequest(SshMsgServiceRequest msg) + throws IOException { + if (acceptServices.containsKey(msg.getServiceName())) { + Service service = (Service) acceptServices.get(msg.getServiceName()); + service.init(Service.ACCEPTING_SERVICE, this); + service.start(); + } else { + this.sendDisconnect(SshMsgDisconnect.SERVICE_NOT_AVAILABLE, + msg.getServiceName() + " is not available"); + } + } +} diff --git a/src/com/sshtools/daemon/util/StringExaminer.java b/src/com/sshtools/daemon/util/StringExaminer.java new file mode 100644 index 0000000000000000000000000000000000000000..353b4a5db9c4282632934aa701fa9bd2ce9f641e --- /dev/null +++ b/src/com/sshtools/daemon/util/StringExaminer.java @@ -0,0 +1,277 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// =========================================================================== +// CONTENT : CLASS StringExaminer +// AUTHOR : Manfred Duchrow +// VERSION : 1.0 - 29/09/2002 +// HISTORY : +// 29/09/2002 duma CREATED +// +// Copyright (c) 2002, by Manfred Duchrow. All rights reserved. +// =========================================================================== +package com.sshtools.daemon.util; + + +// =========================================================================== +// IMPORTS +// =========================================================================== + +/** + * As a subclass of StringScanner this class allows more advanced navigation + * over the underlying string. <br> + * That includes moving to positions of specific substrings etc. + * + * @author Manfred Duchrow + * @version $Id: StringExaminer.java,v 1.7 2003/09/11 15:37:07 martianx Exp $ + */ +public class StringExaminer extends StringScanner { + // ========================================================================= + // CONSTANTS + // ========================================================================= + // ========================================================================= + // INSTANCE VARIABLES + // ========================================================================= + private boolean ignoreCase = false; + + // ========================================================================= + // CLASS METHODS + // ========================================================================= + // ========================================================================= + // CONSTRUCTORS + // ========================================================================= + + /** + * Initialize the new instance with the string to examine. <br> + * The string will be treated case-sensitive. + * + * @param stringToExamine The string that should be examined + */ + public StringExaminer(String stringToExamine) { + this(stringToExamine, false); + } + + // StringExaminer() + // ------------------------------------------------------------------------- + + /** + * Initialize the new instance with the string to examine. + * + * @param stringToExamine The string that should be examined + * @param ignoreCase Specified whether or not treating the string case + * insensitive + */ + public StringExaminer(String stringToExamine, boolean ignoreCase) { + super(stringToExamine); + this.ignoreCase(ignoreCase); + } + + // StringExaminer() + + /** + * + * + * @return + */ + protected boolean ignoreCase() { + return ignoreCase; + } + + /** + * + * + * @param newValue + */ + protected void ignoreCase(boolean newValue) { + ignoreCase = newValue; + } + + // ------------------------------------------------------------------------- + // ========================================================================= + // PUBLIC INSTANCE METHODS + // ========================================================================= + + /** + * Increments the position pointer up to the last character that matched + * the character sequence in the given matchString. Returns true, if the + * matchString was found, otherwise false. + * + * <p> + * If the matchString was found, the next invocation of method nextChar() + * returns the first character after that matchString. + * </p> + * + * @param matchString The string to look up + * + * @return + */ + public boolean skipAfter(String matchString) { + char ch = '-'; + char matchChar = ' '; + boolean found = false; + int index = 0; + + if ((matchString == null) || (matchString.length() == 0)) { + return false; + } + + ch = this.nextChar(); + + while ((endNotReached(ch)) && (!found)) { + matchChar = matchString.charAt(index); + + if (this.charsAreEqual(ch, matchChar)) { + index++; + + if (index >= matchString.length()) { // whole matchString checked ? + found = true; + } else { + ch = this.nextChar(); + } + } else { + if (index == 0) { + ch = this.nextChar(); + } else { + index = 0; + } + } + } + + return found; + } + + // skipAfter() + // ------------------------------------------------------------------------- + + /** + * Increments the position pointer up to the first character before the + * character sequence in the given matchString. Returns true, if the + * matchString was found, otherwise false. + * + * <p> + * If the matchString was found, the next invocation of method nextChar() + * returns the first character of that matchString from the position where + * it was found inside the examined string. + * </p> + * + * @param matchString The string to look up + * + * @return + */ + public boolean skipBefore(String matchString) { + boolean found; + found = this.skipAfter(matchString); + + if (found) { + this.skip(0 - matchString.length()); + } + + return found; + } + + // skipBefore() + // ------------------------------------------------------------------------- + + /** + * Returns the a string containing all characters from the current position + * up to the end of the examined string. <br> + * The character position of the examiner is not changed by this method. + * + * @return + */ + public String peekUpToEnd() { + return this.upToEnd(true); + } + + // peekUpToEnd() + // ------------------------------------------------------------------------- + + /** + * Returns the a string containing all characters from the current position + * up to the end of the examined string. <br> + * The character position is put to the end by this method. That means the + * next invocation of nextChar() returns END_REACHED. + * + * @return + */ + public String upToEnd() { + return this.upToEnd(false); + } + + // upToEnd() + + /** + * + * + * @param char1 + * @param char2 + * + * @return + */ + protected boolean charsAreEqual(char char1, char char2) { + return (this.ignoreCase()) + ? (Character.toUpperCase(char1) == Character.toUpperCase(char2)) + : (char1 == char2); + } + + // charsAreEqual() + // ------------------------------------------------------------------------- + + /** + * Returns the a string containing all characters from the current position + * up to the end of the examined string. <br> + * Depending on the peek flag the character position of the examiner is + * unchanged (true) after calling this method or points behind the strings + * last character. + * + * @param peek + * + * @return + */ + protected String upToEnd(boolean peek) { + char result = '-'; + int lastPosition = 0; + StringBuffer buffer = new StringBuffer(100); + lastPosition = this.getPosition(); + result = this.nextChar(); + + while (endNotReached(result)) { + buffer.append(result); + result = this.nextChar(); + } + + if (peek) { + this.setPosition(lastPosition); + } + + return buffer.toString(); + } + + // upToEnd() + // ------------------------------------------------------------------------- +} + + +// class StringExaminer diff --git a/src/com/sshtools/daemon/util/StringPattern.java b/src/com/sshtools/daemon/util/StringPattern.java new file mode 100644 index 0000000000000000000000000000000000000000..44b41c4bd68da149c299bce49610d6ff9d70f4d1 --- /dev/null +++ b/src/com/sshtools/daemon/util/StringPattern.java @@ -0,0 +1,567 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// =========================================================================== +// CONTENT : CLASS StringPattern +// AUTHOR : Manfred Duchrow +// VERSION : 1.7 - 13/02/2003 +// HISTORY : +// 24/01/2000 duma CREATED +// 08/01/2002 duma bugfix -> Handle *xxx (equal characters after star) correctly +// 16/01/2002 duma changed -> Implements Serializable +// 06/07/2002 duma bugfix -> Couldn't match "London" on "L*n" +// 19/09/2002 duma bugfix -> Couldn't match "MA_DR_HRBLUB" on "*_HR*" +// 19/09/2002 duma changed -> Using now StringExaminer instead of CharacterIterator +// 29/09/2002 duma changed -> Refactored: Using StringExaminer instead of StringScanner +// 26/12/2002 duma changed -> Comment of matches() was wrong / new hasWildcard() +// 13/02/2003 duma added -> setDigitWildcardChar() +// +// Copyright (c) 2000-2003, by Manfred Duchrow. All rights reserved. +// =========================================================================== +package com.sshtools.daemon.util; + + +// =========================================================================== +// IMPORTS +// =========================================================================== +import java.io.*; + + +/** + * This class provides services for checking strings against string-patterns. + * Currently it supports the wildcards<br> + * '' for any number of any character and <br> + * '?' for any one character. The API is very simple:<br> + * <br> + * There are only the two class methods <i>match()</i> and + * <i>matchIgnoreCase()</i>. <br> + * Example: <br> + * StringPattern.match( 'Hello World", "H W" ) ; --> evaluates to true <br> + * StringPattern.matchIgnoreCase( 'StringPattern", "str???pat" ) ; --> + * evaluates to true <br> + * + * @author Manfred Duchrow + * @version 1.7 + */ +public class StringPattern implements Serializable { + // ========================================================================= + // CONSTANTS + // ========================================================================= + + /** */ + protected final static String MULTI_WILDCARD = "*"; + + /** */ + protected final static char MULTICHAR_WILDCARD = '*'; + + /** */ + protected final static char SINGLECHAR_WILDCARD = '?'; + + // ========================================================================= + // INSTANCE VARIABLES + // ========================================================================= + private boolean ignoreCase = false; + private String pattern = null; + + // ------------------------------------------------------------------------- + private Character digitWildcard = null; + + // ------------------------------------------------------------------------- + // ========================================================================= + // CONSTRUCTORS + // ========================================================================= + + /** + * Initializes the new instance with the string pattern and the selecteion, + * if case should be ignored when comparing characters. + * + * @param pattern The pattern to check against ( May contain '' and '?' + * wildcards ) + * @param ignoreCase Definition, if case sensitive character comparison or + * not. + */ + public StringPattern(String pattern, boolean ignoreCase) { + this.setPattern(pattern); + this.setIgnoreCase(ignoreCase); + } + + // StringPattern() + // ------------------------------------------------------------------------- + + /** + * Initializes the new instance with the string pattern. The default is + * case sensitive checking. + * + * @param pattern The pattern to check against ( May contain '' and '?' + * wildcards ) + */ + public StringPattern(String pattern) { + this(pattern, false); + } + + // StringPattern() + // ------------------------------------------------------------------------- + + /** + * Initializes the new instance with the string pattern and a digit + * wildcard character. The default is case sensitive checking. + * + * @param pattern The pattern to check against ( May contain '', '?' + * wildcards and the digit wildcard ) + * @param digitWildcard A wildcard character that stands as placeholder for + * digits + */ + public StringPattern(String pattern, char digitWildcard) { + this(pattern, false, digitWildcard); + } + + // StringPattern() + // ------------------------------------------------------------------------- + + /** + * Initializes the new instance with the string pattern and the selecteion, + * if case should be ignored when comparing characters plus a wildcard + * character for digits. + * + * @param pattern The pattern to check against ( May contain '' and '?' + * wildcards ) + * @param ignoreCase Definition, if case sensitive character comparison or + * not. + * @param digitWildcard A wildcard character that stands as placeholder for + * digits + */ + public StringPattern(String pattern, boolean ignoreCase, char digitWildcard) { + this.setPattern(pattern); + this.setIgnoreCase(ignoreCase); + this.setDigitWildcardChar(digitWildcard); + } + + // StringPattern() + + /** + * Returns whether or not the pattern matching ignores upper and lower case + * + * @return + */ + public boolean getIgnoreCase() { + return ignoreCase; + } + + /** + * Sets whether the pattern matching should ignore case or not + * + * @param newValue + */ + public void setIgnoreCase(boolean newValue) { + ignoreCase = newValue; + } + + /** + * Returns the pattern as string. + * + * @return + */ + public String getPattern() { + return pattern; + } + + /** + * Sets the pattern to a new value + * + * @param newValue + */ + public void setPattern(String newValue) { + pattern = newValue; + } + + /** + * + * + * @return + */ + protected Character digitWildcard() { + return digitWildcard; + } + + /** + * + * + * @param newValue + */ + protected void digitWildcard(Character newValue) { + digitWildcard = newValue; + } + + // ========================================================================= + // CLASS METHODS + // ========================================================================= + + /** + * Returns true, if the given probe string matches the given pattern. <br> + * The character comparison is done case sensitive. + * + * @param probe The string to check against the pattern. + * @param pattern The patter, that probably contains wildcards ( '' or '?' + * ) + * + * @return + */ + public static boolean match(String probe, String pattern) { + StringPattern stringPattern = new StringPattern(pattern, false); + + return (stringPattern.matches(probe)); + } + + // match() + // ------------------------------------------------------------------------- + + /** + * Returns true, if the given probe string matches the given pattern. <br> + * The character comparison is done ignoring upper/lower-case. + * + * @param probe The string to check against the pattern. + * @param pattern The patter, that probably contains wildcards ( '' or '?' + * ) + * + * @return + */ + public static boolean matchIgnoreCase(String probe, String pattern) { + StringPattern stringPattern = new StringPattern(pattern, true); + + return (stringPattern.matches(probe)); + } + + // matchIgnoreCase() + // ------------------------------------------------------------------------- + // ========================================================================= + // PUBLIC INSTANCE METHODS + // ========================================================================= + + /** + * Tests if a specified string matches the pattern. + * + * @param probe The string to compare to the pattern + * + * @return true if and only if the probe matches the pattern, false + * otherwise. + */ + public boolean matches(String probe) { + StringExaminer patternIterator = null; + StringExaminer probeIterator = null; + char patternCh = '-'; + char probeCh = '-'; + String newPattern = null; + String subPattern = null; + int charIndex = 0; + + if (probe == null) { + return false; + } + + if (probe.length() == 0) { + return false; + } + + patternIterator = this.newExaminer(this.getPattern()); + probeIterator = this.newExaminer(probe); + probeCh = probeIterator.nextChar(); + patternCh = this.getPatternChar(patternIterator, probeCh); + + while ((this.endNotReached(patternCh)) && + (this.endNotReached(probeCh))) { + if (patternCh == MULTICHAR_WILDCARD) { + patternCh = this.skipWildcards(patternIterator); + + if (this.endReached(patternCh)) { + return true; // No more characters after multi wildcard - So everything matches + } else { + patternIterator.skip(-1); + newPattern = this.upToEnd(patternIterator); + charIndex = newPattern.indexOf(MULTICHAR_WILDCARD); + + if (charIndex >= 0) { + subPattern = newPattern.substring(0, charIndex); + + if (this.skipAfter(probeIterator, subPattern)) { + patternIterator = this.newExaminer(newPattern.substring( + charIndex)); + patternCh = probeCh; + } else { + return false; + } + } else { + probeIterator.skip(-1); + + return this.matchReverse(newPattern, probeIterator); + } + } + } + + if (this.charsAreEqual(probeCh, patternCh)) { + if (this.endNotReached(patternCh)) { + probeCh = probeIterator.nextChar(); + patternCh = this.getPatternChar(patternIterator, probeCh); + } + } else { + if (patternCh != MULTICHAR_WILDCARD) { + return false; // character is not matching - return immediately + } + } + } + + // while() + return ((this.endReached(patternCh)) && (this.endReached(probeCh))); + } + + // matches() + // ------------------------------------------------------------------------- + + /** + * Returns the pattern string. + * + * @see java.lang.Object#toString() + */ + public String toString() { + if (this.getPattern() == null) { + return super.toString(); + } else { + return this.getPattern(); + } + } + + // toString() + // ------------------------------------------------------------------------- + + /** + * Returns true if the pattern contains any '' or '?' wildcard character. + * + * @return + */ + public boolean hasWildcard() { + if (this.getPattern() == null) { + return false; + } + + if (this.hasDigitWildcard()) { + if (this.getPattern().indexOf(this.digitWildcardChar()) >= 0) { + return true; + } + } + + return (this.getPattern().indexOf(MULTI_WILDCARD) >= 0) || + (this.getPattern().indexOf(SINGLECHAR_WILDCARD) >= 0); + } + + // hasWildcard() + // ------------------------------------------------------------------------- + + /** + * Sets the given character as a wildcard character in this pattern to + * match only digits ('0'-'9'). <br> + * + * @param digitWildcard The placeholder character for digits + */ + public void setDigitWildcardChar(char digitWildcard) { + if (digitWildcard <= 0) { + this.digitWildcard(null); + } else { + this.digitWildcard(new Character(digitWildcard)); + } + } + + // setDigitWildcardChar() + + /** + * + * + * @return + */ + protected boolean hasDigitWildcard() { + return this.digitWildcard() != null; + } + + // hasDigitWildcard() + // ------------------------------------------------------------------------- + protected char digitWildcardChar() { + if (this.hasDigitWildcard()) { + return this.digitWildcard().charValue(); + } else { + return '\0'; + } + } + + // digitWildcardChar() + // ------------------------------------------------------------------------- + + /** + * Moves the iterator position to the next character that is no wildcard. + * Doesn't skip digit wildcards ! + * + * @param iterator + * + * @return + */ + protected char skipWildcards(StringExaminer iterator) { + char result = '-'; + + do { + result = iterator.nextChar(); + } while ((result == MULTICHAR_WILDCARD) || + (result == SINGLECHAR_WILDCARD)); + + return result; + } + + // skipWildcards() + // ------------------------------------------------------------------------- + + /** + * Increments the given iterator up to the last character that matched the + * character sequence in the given matchString. Returns true, if the + * matchString was found, otherwise false. + * + * @param examiner + * @param matchString The string to be found (must not contain ) + * + * @return + */ + protected boolean skipAfter(StringExaminer examiner, String matchString) { + // Do not use the method of StringExaminer anymore, because digit wildcard + // support is in the charsAreEqual() method which is unknown to the examiner. + // return examiner.skipAfter( matchString ) ; + char ch = '-'; + char matchChar = ' '; + boolean found = false; + int index = 0; + + if ((matchString == null) || (matchString.length() == 0)) { + return false; + } + + ch = examiner.nextChar(); + + while ((examiner.endNotReached(ch)) && (!found)) { + matchChar = matchString.charAt(index); + + if (this.charsAreEqual(ch, matchChar)) { + index++; + + if (index >= matchString.length()) { // whole matchString checked ? + found = true; + } else { + ch = examiner.nextChar(); + } + } else { + if (index == 0) { + ch = examiner.nextChar(); + } else { + index = 0; + } + } + } + + return found; + } + + // skipAfter() + // ------------------------------------------------------------------------- + protected String upToEnd(StringExaminer iterator) { + return iterator.upToEnd(); + } + + // upToEnd() + // ------------------------------------------------------------------------- + protected boolean matchReverse(String pattern, StringExaminer probeIterator) { + String newPattern; + String newProbe; + StringPattern newMatcher; + newPattern = MULTI_WILDCARD + pattern; + newProbe = this.upToEnd(probeIterator); + newPattern = this.strUtil().reverse(newPattern); + newProbe = this.strUtil().reverse(newProbe); + newMatcher = new StringPattern(newPattern, this.getIgnoreCase()); + + if (this.hasDigitWildcard()) { + newMatcher.setDigitWildcardChar(this.digitWildcardChar()); + } + + return newMatcher.matches(newProbe); + } + + // matchReverse() + // ------------------------------------------------------------------------- + protected boolean charsAreEqual(char probeChar, char patternChar) { + if (this.hasDigitWildcard()) { + if (patternChar == this.digitWildcardChar()) { + return Character.isDigit(probeChar); + } + } + + if (this.getIgnoreCase()) { + return (Character.toUpperCase(probeChar) == Character.toUpperCase(patternChar)); + } else { + return (probeChar == patternChar); + } + } + + // charsAreEqual() + // ------------------------------------------------------------------------- + protected boolean endReached(char character) { + return (character == StringExaminer.END_REACHED); + } + + // endReached() + // ------------------------------------------------------------------------- + protected boolean endNotReached(char character) { + return (!endReached(character)); + } + + // endNotReached() + // ------------------------------------------------------------------------- + protected char getPatternChar(StringExaminer patternIterator, char probeCh) { + char patternCh; + patternCh = patternIterator.nextChar(); + + return ((patternCh == SINGLECHAR_WILDCARD) ? probeCh : patternCh); + } + + // getPatternChar() + // ------------------------------------------------------------------------- + protected StringExaminer newExaminer(String str) { + return new StringExaminer(str, this.getIgnoreCase()); + } + + // newExaminer() + // ------------------------------------------------------------------------- + protected StringUtil strUtil() { + return StringUtil.current(); + } + + // strUtil() + // ------------------------------------------------------------------------- +} + + +// class StringPattern diff --git a/src/com/sshtools/daemon/util/StringScanner.java b/src/com/sshtools/daemon/util/StringScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..c9e94774bb49fca50898bbf7f0a1980e7b15ce84 --- /dev/null +++ b/src/com/sshtools/daemon/util/StringScanner.java @@ -0,0 +1,286 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// =========================================================================== +// CONTENT : CLASS StringScanner +// AUTHOR : Manfred Duchrow +// VERSION : 1.1 - 29/09/2002 +// HISTORY : +// 11/07/2001 duma CREATED +// 29/09/2002 duma added -> endReached(), endNotReached() +// +// Copyright (c) 2001-2002, by Manfred Duchrow. All rights reserved. +// =========================================================================== +package com.sshtools.daemon.util; + + +// =========================================================================== +// IMPORTS +// =========================================================================== + +/** + * Simple scanner that allows to navigate over the characters of a string. + * + * @author Manfred Duchrow + * @version 1.1 + */ +public class StringScanner { + // ========================================================================= + // CONSTANTS + // ========================================================================= + + /** */ + public static final char END_REACHED = (char) -1; + + // ========================================================================= + // INSTANCE VARIABLES + // ========================================================================= + + /** */ + protected int length = 0; + + /** */ + protected int position = 0; + + /** */ + protected int pos_marker = 0; + + /** */ + protected char[] buffer = null; + + // ------------------------------------------------------------------------- + // ========================================================================= + // CONSTRUCTORS + // ========================================================================= + + /** + * Initialize the new instance with the string that should be scanned. + * + * @param stringToScan + */ + public StringScanner(String stringToScan) { + super(); + length = stringToScan.length(); + buffer = new char[length]; + stringToScan.getChars(0, length, buffer, 0); + } + + // StringScanner() + // ========================================================================= + // PUBLIC CLASS METHODS + // ========================================================================= + + /** + * Returns true, if the given character indicates that the end of the + * scanned string is reached. + * + * @param character + * + * @return + */ + public boolean endReached(char character) { + return (character == END_REACHED); + } + + // endReached() + // ------------------------------------------------------------------------- + + /** + * Returns true, if the given character does <b>not</b> indicate that the + * end of the scanned string si reached. + * + * @param character + * + * @return + */ + public boolean endNotReached(char character) { + return (!endReached(character)); + } + + // endNotReached() + // ========================================================================= + // PUBLIC INSTANCE METHODS + // ========================================================================= + + /** + * Returns the string the scanner was initialized with + * + * @return + */ + public String toString() { + return new String(buffer); + } + + // toString() + // ------------------------------------------------------------------------- + + /** + * Moves the position pointer count characters. positive values move + * forwards, negative backwards. The position never becomes negative ! + * + * @param count + */ + public void skip(int count) { + position += count; + + if (position < 0) { + position = 0; + } + } + + // skip() + // ------------------------------------------------------------------------- + + /** + * Returns the character at the current position without changing the + * position, that is subsequent calls to this method return always the + * same character. + * + * @return + */ + public char peek() { + return ((position < length()) ? buffer[position] : END_REACHED); + } + + // skip() + // ------------------------------------------------------------------------- + + /** + * Returns the character at the current position and increments the + * position afterwards by 1. + * + * @return + */ + public char nextChar() { + char next = this.peek(); + + if (endNotReached(next)) { + this.skip(1); + } + + return next; + } + + // nextChar() + // ------------------------------------------------------------------------- + + /** + * Returns true, if the scanner has reached the end and a further + * invocation of nextChar() would return the END_REACHED character. + * + * @return + */ + public boolean atEnd() { + return (endReached(this.peek())); + } + + // atEnd() + // ------------------------------------------------------------------------- + + /** + * Returns true, if the scanner has not yet reached the end. + * + * @return + */ + public boolean hasNext() { + return !this.atEnd(); + } + + // hasNext() + // ------------------------------------------------------------------------- + + /** + * Returns the next character that is no whitespace and leaves the position + * pointer one character after the returned one. + * + * @return + */ + public char nextNoneWhitespaceChar() { + char next = this.nextChar(); + + while ((endNotReached(next)) && (Character.isWhitespace(next))) { + next = this.nextChar(); + } + + return next; + } + + // nextNoneWhitespaceChar() + // ------------------------------------------------------------------------- + + /** + * Returns the current position in the string + * + * @return + */ + public int getPosition() { + return position; + } + + // getPosition() + // ------------------------------------------------------------------------- + + /** + * Remembers the current position for later use with restorePosition() + */ + public void markPosition() { + pos_marker = position; + } + + // markPosition() + // ------------------------------------------------------------------------- + + /** + * Restores the position to the value of the latest markPosition() call + */ + public void restorePosition() { + this.setPosition(pos_marker); + } + + // restorePosition() + + /** + * + * + * @return + */ + protected int length() { + return length; + } + + // length() + // ------------------------------------------------------------------------- + protected void setPosition(int pos) { + if ((pos >= 0) && (pos <= this.length())) { + position = pos; + } + } + + // setPosition() + // ------------------------------------------------------------------------- +} + + +// class StringScanner diff --git a/src/com/sshtools/daemon/util/StringUtil.java b/src/com/sshtools/daemon/util/StringUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..235601d9089fed5e346f34a1aadb92b98f742d49 --- /dev/null +++ b/src/com/sshtools/daemon/util/StringUtil.java @@ -0,0 +1,1871 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// =========================================================================== +// CONTENT : CLASS StringUtil +// AUTHOR : Manfred Duchrow +// VERSION : 2.0 - 21/03/2003 +// HISTORY : +// 10/07/1999 duma CREATED +// 09/12/1999 duma added -> SPACE, repeat() +// moved -> from package com.mdcs.util +// 25/01/2000 duma moved -> from package com.mdcs.text +// 09/02/2000 duma changed -> renamed SPACE to CH_SPACE +// added -> CH_CR, CH_TAB, ..., STR_SPACE, STR_NEWLINE, ... +// 11/01/2002 duma added -> indexOf(), indexOfIgnoreCase(), contains(), containsIgnoreCase() +// 17/05/2002 duma added -> copyFrom() +// 03/07/2002 duma added -> cutHead(), prefix(), suffix() +// 06/07/2002 duma added -> indexOf() and contains() for StringPattern and reverse() +// 15/08/2002 duma added -> upTo(), startingFrom(), asMap() +// 29/09/2002 duma added -> allParts() and allSubstrings() that don't skip empty elements +// 06/03/2003 duma changed -> append() now uses System.arraycopy() +// added -> remove( String[], String[] ), remove( String[], String ) +// removeNull( String[] ) +// 21/03/2003 duma added -> leftPad(), leftPadCh(), rightPad(), rightPadCh() for int values +// +// Copyright (c) 1999-2003, by Manfred Duchrow. All rights reserved. +// =========================================================================== +package com.sshtools.daemon.util; + +import java.io.*; + +import java.text.*; + +import java.util.*; + + +/** + * The sole instance of this class provides several convienience methods for + * string manipulation such as substring replacement or character repetition. + * + * @author Manfred Duchrow + * @version 2.0 + */ +public class StringUtil { + // ========================================================================= + // CONSTANTS + // ========================================================================= + + /** Constant for the space character */ + public static final char CH_SPACE = '\u0020'; + + /** Constant for the new line character */ + public static final char CH_NEWLINE = '\n'; + + /** Constant for the carriage return character */ + public static final char CH_CR = '\r'; + + /** Constant for the tabulator character */ + public static final char CH_TAB = '\t'; + + /** Constant for the String representation of the space character */ + public static final String STR_SPACE = "\u0020"; + + /** Constant for the String representation of the new line character */ + public static final String STR_NEWLINE = "\n"; + + /** + * Constant for the String representation of the carriage return character + */ + public static final String STR_CR = "\r"; + + /** Constant for the String representation of the tabulator character */ + public static final String STR_TAB = "\t"; + private static final String WORD_DELIM = STR_SPACE + STR_TAB + STR_NEWLINE + + STR_CR; + + // ========================================================================= + // CLASS VARIABLES + // ========================================================================= + private static StringUtil singleton = null; + + private static StringUtil getSingleton() { + return singleton; + } + + private static void setSingleton(StringUtil inst) { + singleton = inst; + } + + // ========================================================================= + // PUBLIC CLASS METHODS + // ========================================================================= + + /** + * Returns the one and only instance of this class. + * + * @return + */ + public static StringUtil current() { + if (getSingleton() == null) { + setSingleton(new StringUtil()); + } + + return getSingleton(); + } + + // current() + // ========================================================================= + // PUBLIC INSTANCE METHODS + // ========================================================================= + + /** + * Returns the given string with all found oldSubStr replaced by newSubStr. <br> + * Example: StringUtil.current().replaceAll( "Seven of ten", "even", "ix" + * ) ;<br> + * results in: "Six of ten" + * + * @param sourceStr The string that should be checked for occurrences of + * oldSubStr + * @param oldSubStr The string that is searched for in sourceStr + * @param newSubStr The new string that is placed everywhere the oldSubStr + * was found + * + * @return The original string with all found substrings replaced by new + * strings + */ + public String replaceAll(String sourceStr, String oldSubStr, + String newSubStr) { + String part = null; + String result = ""; + int index = -1; + int subLen = 0; + subLen = oldSubStr.length(); + part = sourceStr; + + while ((part.length() > 0) && (subLen > 0)) { + index = part.indexOf(oldSubStr); + + if (index >= 0) { + result = result + part.substring(0, index) + newSubStr; + part = part.substring(index + subLen); + } else { + result = result + part; + part = ""; + } + } + + // while + return result; + } + + // replaceAll() + // ------------------------------------------------------------------------- + + /** + * Returns a string with size of count and all characters initialized with + * ch. <br> + * + * @param ch the character to be repeated in the result string. + * @param count the number of times the given character should occur in the + * result string. + * + * @return A string containing <i>count</i> characters <i>ch</i>. + */ + public String repeat(char ch, int count) { + StringBuffer buffer = null; + buffer = new StringBuffer(count); + + for (int i = 1; i <= count; i++) { + buffer.append(ch); + } + + return (buffer.toString()); + } + + // repeat() + // ------------------------------------------------------------------------- + + /** + * Returns an array of substrings of the given text. <br> + * The delimiters between the substrings are the whitespace characters + * SPACE, NEWLINE, CR and TAB. + * + * @param text The string that should be splitted into whitespace separated + * words + * + * @return An array of substrings of the given text + * + * @see #parts(String, String) + */ + public String[] words(String text) { + return this.parts(text, WORD_DELIM); + } + + // words() + // ------------------------------------------------------------------------- + + /** + * Returns an array of substrings of the given text. <br> + * The separators between the substrings are the given delimiters. Each + * character in the delimiter string is treated as a separator. <br> + * All consecutive delimiters are treated as one delimiter, that is there + * will be no empty strings in the result. + * + * @param text The string that should be splitted into substrings + * @param delimiters All characters that should be recognized as a + * separator or substrings + * + * @return An array of substrings of the given text + * + * @see #allParts(String, String) + * @see #substrings(String, String) + * @see #allSubstrings(String, String) + */ + public String[] parts(String text, String delimiters) { + return this.parts(text, delimiters, false); + } + + // parts() + // ------------------------------------------------------------------------- + + /** + * Returns an array of substrings of the given text. <br> + * The separators between the substrings are the given delimiters. Each + * character in the delimiter string is treated as a separator. <br> + * For each delimiter that is followed immediately by another delimiter an + * empty string will be added to the result. There are no empty strings + * added to the result for a delimiter at the very beginning of at the + * very end. + * + * <p> + * Examples: + * </p> + * + * <p> + * allParts( "/A/B//", "/" ) --> { "A", "B", "" }<br> + * allParts( "/A,B/C;D", ",;/" ) --> { "A", "B", "C", "D" }<br> + * allParts( "A/B,C/D", "," ) --> { "A/B", "C/D" }<br> + * </p> + * + * @param text The string that should be splitted into substrings + * @param delimiters All characters that should be recognized as a + * separator or substrings + * + * @return An array of substrings of the given text + * + * @see #parts(String, String) + * @see #substrings(String, String) + * @see #allSubstrings(String, String) + */ + public String[] allParts(String text, String delimiters) { + return this.parts(text, delimiters, true); + } + + // allParts() + // ------------------------------------------------------------------------- + + /** + * Returns the given text split up into an array of strings, at the + * occurrances of the separator string. In contrary to method parts() the + * separator is a one or many character sequence delimiter. That is, only + * the exact sequence of the characters in separator identifies the end + * of a substring. Subsequent occurences of separator will be skipped. + * Therefore no empty strings ("") will be in the result array. + * + * @param text The text to be split up + * @param separator The string that separates the substrings + * + * @return An array of substrings not containing any separator anymore + * + * @see #allSubstrings(String, String) + * @see #parts(String, String) + * @see #allParts(String, String) + */ + public String[] substrings(String text, String separator) { + return this.substrings(text, separator, false); + } + + // substrings() + // ------------------------------------------------------------------------- + + /** + * Returns the given text split up into an array of strings, at the + * occurrances of the separator string. In contrary to method allParts() + * the separator is a one or many character sequence delimiter. That is, + * only the exact sequence of the characters in separator identifies the + * end of a substring. Subsequent occurences of separator are not skipped. + * They are added as empty strings to the result. + * + * @param text The text to be split up + * @param separator The string that separates the substrings + * + * @return An array of substrings not containing any separator anymore + * + * @see #substrings(String, String) + * @see #parts(String, String) + * @see #allParts(String, String) + */ + public String[] allSubstrings(String text, String separator) { + return this.substrings(text, separator, true); + } + + // allSubstrings() + // ------------------------------------------------------------------------- + + /** + * Returns the first substring that is enclosed by the specified + * delimiters. <br> + * The delimiters are not included in the return string. + * + * <p> + * Example:<br> getDelimitedSubstring( "This {placeholder} belongs to me", + * "{", "}" ) --> returns "placeholder" + * </p> + * + * @param text The input string that contains the delimited part + * @param startDelimiter The start delimiter of the substring + * @param endDelimiter The end delimiter of the substring + * + * @return The substring or an empty string, if no delimiters are found. + */ + public String getDelimitedSubstring(String text, String startDelimiter, + String endDelimiter) { + int start; + int stop; + String subStr = ""; + + if ((text != null) && (startDelimiter != null) && + (endDelimiter != null)) { + start = text.indexOf(startDelimiter); + + if (start >= 0) { + stop = text.indexOf(endDelimiter, start + 1); + + if (stop > start) { + subStr = text.substring(start + 1, stop); + } + } + } + + return subStr; + } + + // getDelimitedSubstring() + // ------------------------------------------------------------------------- + + /** + * Returns the first substring that is enclosed by the specified delimiter. <br> + * The delimiters are not included in the return string. + * + * <p> + * Example:<br> getDelimitedSubstring( "File 'text.txt' not found.", "'", + * "'" ) --> returns "text.txt" + * </p> + * + * @param text The input string that contains the delimited part + * @param delimiter The start and end delimiter of the substring + * + * @return The substring or an empty string, if no delimiters are found. + */ + public String getDelimitedSubstring(String text, String delimiter) { + return this.getDelimitedSubstring(text, delimiter, delimiter); + } + + // getDelimitedSubstring() + // ------------------------------------------------------------------------- + + /** + * Prints the stack trace of the specified throwable to a string and + * returns it. + * + * @param throwable + * + * @return + */ + public String stackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + throwable.printStackTrace(pw); + pw.close(); + + return sw.toString(); + } + + // stackTrace() + // ------------------------------------------------------------------------- + + /** + * Returns the given string filled (on the left) up to the specified length + * with the given character. <br> + * Example: leftPadCh( "12", 6, '0' ) --> "000012" + * + * @param str + * @param len + * @param ch + * + * @return + */ + public String leftPadCh(String str, int len, char ch) { + return this.padCh(str, len, ch, true); + } + + // leftPadCh() + // ------------------------------------------------------------------------- + + /** + * Returns the given string filled (on the left) up to the specified length + * with spaces. <br> + * Example: leftPad( "XX", 4 ) --> " XX" + * + * @param str The string that has to be filled up to the specified length + * @param len The length of the result string + * + * @return + */ + public String leftPad(String str, int len) { + return this.leftPadCh(str, len, CH_SPACE); + } + + // leftPad() + // ------------------------------------------------------------------------- + + /** + * Returns the given integer as string filled (on the left) up to the + * specified length with the given fill character. <br> + * Example: leftPad( 24, 5, '' ) --> "24" + * + * @param value + * @param len + * @param fillChar + * + * @return + */ + public String leftPadCh(int value, int len, char fillChar) { + return this.leftPadCh(Integer.toString(value), len, fillChar); + } + + // leftPadCh() + // ------------------------------------------------------------------------- + + /** + * Returns the given integer as string filled (on the left) up to the + * specified length with zeroes. <br> + * Example: leftPad( 12, 4 ) --> "0012" + * + * @param value + * @param len + * + * @return + */ + public String leftPad(int value, int len) { + return this.leftPadCh(value, len, '0'); + } + + // leftPad() + // ------------------------------------------------------------------------- + + /** + * Returns the given string filled (on the right) up to the specified + * length with the given character. <br> + * Example: rightPadCh( "34", 5, 'X' ) --> "34XXX" + * + * @param str + * @param len + * @param ch + * + * @return + */ + public String rightPadCh(String str, int len, char ch) { + return this.padCh(str, len, ch, false); + } + + // rightPadCh() + // ------------------------------------------------------------------------- + + /** + * Returns the given string filled (on the right) up to the specified + * length with spaces. <br> + * Example: rightPad( "88", 6 ) --> "88 " + * + * @param str + * @param len + * + * @return + */ + public String rightPad(String str, int len) { + return this.rightPadCh(str, len, CH_SPACE); + } + + // rightPad() + // ------------------------------------------------------------------------- + + /** + * Returns the given integer as string filled (on the right) up to the + * specified length with the given character. <br> + * Example: rightPad( "32", 4, '#' ) --> "32##" + * + * @param value + * @param len + * @param fillChar + * + * @return + */ + public String rightPadCh(int value, int len, char fillChar) { + return this.rightPadCh(Integer.toString(value), len, fillChar); + } + + // rightPad() + // ------------------------------------------------------------------------- + + /** + * Returns the given integer as string filled (on the right) up to the + * specified length with spaces. <br> + * Example: rightPad( "17", 5 ) --> "17 " + * + * @param value + * @param len + * + * @return + */ + public String rightPad(int value, int len) { + return this.rightPadCh(value, len, CH_SPACE); + } + + // rightPad() + // ------------------------------------------------------------------------- + + /** + * Returns the given string filled equally left and right up to the + * specified length with the given character. <br> + * Example: centerCh( "A", 5, '_' ) --> "__A__" <br> + * Example: centerCh( "XX", 7, '+' ) --> "++XX+++" + * + * @param str + * @param len + * @param ch + * + * @return + */ + public String centerCh(String str, int len, char ch) { + String buffer = null; + int missing = len - str.length(); + int half = 0; + + if (missing <= 0) { + return str; + } + + half = missing / 2; + buffer = this.rightPadCh(str, len - half, ch); + + return this.leftPadCh(buffer, len, ch); + } + + // centerCh() + // ------------------------------------------------------------------------- + + /** + * Returns the given string filled (on the right and right) up to the + * specified length with spaces. <br> + * Example: center( "Mike", 10 ) --> " Mike " + * + * @param str + * @param len + * + * @return + */ + public String center(String str, int len) { + return this.centerCh(str, len, CH_SPACE); + } + + // center() + // ------------------------------------------------------------------------- + + /** + * Returns the given string array extended by one element that hold the + * specified string. + * + * @param strings + * @param string + * + * @return + */ + public String[] append(String[] strings, String string) { + String[] appStr = { string }; + + return this.append(strings, appStr); + } + + // append() + // ------------------------------------------------------------------------- + + /** + * Returns an array of strings that contains all strings given by the first + * and second string array. The strings from the second array will be + * added at the end of the first array. + * + * @param strings The array of string to which to append + * @param appendStrings The string to be appended to the first array + * + * @return + */ + public String[] append(String[] strings, String[] appendStrings) { + String[] newStrings = null; + + if (strings == null) { + return appendStrings; + } + + if (appendStrings == null) { + return strings; + } + + newStrings = new String[strings.length + appendStrings.length]; + System.arraycopy(strings, 0, newStrings, 0, strings.length); + System.arraycopy(appendStrings, 0, newStrings, strings.length, + appendStrings.length); + + return newStrings; + } + + // append() + // ------------------------------------------------------------------------- + + /** + * Returns an array of strings that contains all strings given in the first + * plus the specified string to append, if it is not already in the given + * array. + * + * @param strings + * @param appendString + * + * @return + */ + public String[] appendIfNotThere(String[] strings, String appendString) { + if (this.contains(strings, appendString)) { + return strings; + } else { + return this.append(strings, appendString); + } + } + + // appendIfNotThere() + // ------------------------------------------------------------------------- + + /** + * Returns an array of strings that contains all strings given in the first + * plus all strings of the second array that are not already in the first + * array. + * + * @param strings + * @param appendStrings + * + * @return + */ + public String[] appendIfNotThere(String[] strings, String[] appendStrings) { + String[] newStrings = strings; + + if (appendStrings == null) { + return newStrings; + } + + for (int i = 0; i < appendStrings.length; i++) { + newStrings = this.appendIfNotThere(newStrings, appendStrings[i]); + } + + return newStrings; + } + + // appendIfNotThere() + // ------------------------------------------------------------------------- + + /** + * Removes all string of the second array from the first array. Returns a + * new array of string that contains all remaining strings of the original + * strings array. + * + * @param strings The array from which to remove the strings + * @param removeStrings The strings to be removed + * + * @return + */ + public String[] remove(String[] strings, String[] removeStrings) { + if ((strings == null) || (removeStrings == null) || + (strings.length == 0) || (removeStrings.length == 0)) { + return strings; + } + + return this.removeFromStringArray(strings, removeStrings); + } + + // remove() + // ------------------------------------------------------------------------- + + /** + * Removes the given string from the specified string array. Returns a new + * array of string that contains all remaining strings of the original + * strings array. + * + * @param strings The array from which to remove the string + * @param removeString The string to be removed + * + * @return + */ + public String[] remove(String[] strings, String removeString) { + String[] removeStrings = { removeString }; + + return this.remove(strings, removeStrings); + } + + // remove() + // ------------------------------------------------------------------------- + + /** + * Removes all null values from the given string array. Returns a new + * string array that contains all none null values of the input array. + * + * @param strings The array to be cleared of null values + * + * @return + */ + public String[] removeNull(String[] strings) { + if (strings == null) { + return strings; + } + + return this.removeFromStringArray(strings, null); + } + + // removeNull() + // ------------------------------------------------------------------------- + + /** + * Returns a string that contains all given strings concatenated and + * separated by the specified separator. + * + * @param strings The array of strings that should be concatenated + * @param separator The separator between the strings + * + * @return One string containing the concatenated strings separated by + * separator + */ + public String asString(String[] strings, String separator) { + StringBuffer buffer = null; + buffer = new StringBuffer(strings.length * 20); + + if (strings.length > 0) { + buffer.append(strings[0].toString()); + + for (int i = 1; i < strings.length; i++) { + buffer.append(separator); + + if (strings[i] != null) { + buffer.append(strings[i]); + } + } + } + + return buffer.toString(); + } + + // asString() + // ------------------------------------------------------------------------- + + /** + * Returns a string that contains all given strings concatenated and + * separated by comma. + * + * @param strings The array of strings that should be concatenated + * + * @return One string containing the concatenated strings separated by + * comma (",") + */ + public String asString(String[] strings) { + return this.asString(strings, ","); + } + + // asString() + // ------------------------------------------------------------------------- + + /** + * Returns the index of the first string in the given string array that + * matches the specified string pattern. If no string is found in the + * array the result is -1. + * + * @param strArray An array of string (may contain null elements) + * @param pattern The pattern the searched string must match + * + * @return The index of the matching string in the array or -1 if not found + */ + public int indexOf(String[] strArray, StringPattern pattern) { + if ((strArray == null) || (strArray.length == 0)) { + return -1; + } + + boolean found = false; + + for (int i = 0; i < strArray.length; i++) { + if (strArray[i] == null) { + if (pattern == null) { + found = true; + } + } else { + if (pattern != null) { + found = pattern.matches(strArray[i]); + } + } + + if (found) { + return i; + } + } + + return -1; + } + + // indexOf() + // ------------------------------------------------------------------------- + + /** + * Returns the index of the specified string in the given string array. It + * returns the index of the first occurrence of the string. If the string + * is not found in the array the result is -1. The comparison of the + * strings is case-sensitive! + * + * @param strArray An array of string (may contain null elements) + * @param searchStr The string to be looked up in the array (null allowed) + * + * @return The index of the string in the array or -1 if not found + */ + public int indexOf(String[] strArray, String searchStr) { + return this.indexOfString(strArray, searchStr, false); + } + + // indexOf() + // ------------------------------------------------------------------------- + + /** + * Returns the index of the specified string in the given string array. It + * returns the index of the first occurrence of the string. If the string + * is not found in the array the result is -1. The comparison of the + * strings is case-insensitive! + * + * @param strArray An array of string (may contain null elements) + * @param searchStr The string to be looked up in the array (null allowed) + * + * @return The index of the string in the array or -1 if not found + */ + public int indexOfIgnoreCase(String[] strArray, String searchStr) { + return this.indexOfString(strArray, searchStr, true); + } + + // indexOfIgnoreCase() + // ------------------------------------------------------------------------- + + /** + * Returns whether or not the specified string can be found in the given + * string array. + * + * @param strArray An array of string (may contain null elements) + * @param searchStr The string to be looked up in the array (null allowed) + * @param ignoreCase Defines whether or not the comparison is + * case-sensitive. + * + * @return true, if the specified array contains the given string + */ + public boolean contains(String[] strArray, String searchStr, + boolean ignoreCase) { + if (ignoreCase) { + return this.containsIgnoreCase(strArray, searchStr); + } else { + return this.contains(strArray, searchStr); + } + } + + // contains() + // ------------------------------------------------------------------------- + + /** + * Returns whether or not a string can be found in the given string array + * that matches the specified string pattern. + * + * @param strArray An array of string (may contain null elements) + * @param pattern The string pattern to match against in the array (null + * allowed) + * + * @return true, if the specified array contains a string matching the + * pattern + */ + public boolean contains(String[] strArray, StringPattern pattern) { + return (this.indexOf(strArray, pattern) >= 0); + } + + // contains() + // ------------------------------------------------------------------------- + + /** + * Returns whether or not the specified string can be found in the given + * string array. The comparison of the strings is case-sensitive! + * + * @param strArray An array of string (may contain null elements) + * @param searchStr The string to be looked up in the array (null allowed) + * + * @return true, if the specified array contains the given string + */ + public boolean contains(String[] strArray, String searchStr) { + return (this.indexOf(strArray, searchStr) >= 0); + } + + // contains() + // ------------------------------------------------------------------------- + + /** + * Returns whether or not the specified string can be found in the given + * string array. The comparison of the strings is case-insensitive! + * + * @param strArray An array of string (may contain null elements) + * @param searchStr The string to be looked up in the array (null allowed) + * + * @return true, if the specified array contains the given string + */ + public boolean containsIgnoreCase(String[] strArray, String searchStr) { + return (this.indexOfIgnoreCase(strArray, searchStr) >= 0); + } + + // containsIgnoreCase() + // ------------------------------------------------------------------------- + + /** + * Returns all elements of string array <i>from</i> in a new array from + * index start up to the end. If start index is larger than the array's + * length, an empty array will be returned. + * + * @param from The string array the elements should be copied from + * @param start Index of the first element to copy + * + * @return + */ + public String[] copyFrom(String[] from, int start) { + if (from == null) { + return null; + } + + return this.copyFrom(from, start, from.length - 1); + } + + // copyFrom() + // ------------------------------------------------------------------------- + + /** + * Returns all elements of string array <i>from</i> in a new array from + * index start up to index end (inclusive). If end is larger than the last + * valid index, it will be reduced to the last index. If end index is less + * than start index, an empty array will be returned. + * + * @param from The string array the elements should be copied from + * @param start Index of the first element to copy + * @param end Index of last element to be copied + * + * @return + */ + public String[] copyFrom(String[] from, int start, int end) { + String[] result; + int count; + int stop = end; + + if (from == null) { + return null; + } + + if (stop > (from.length - 1)) { + stop = from.length - 1; + } + + count = stop - start + 1; + + if (count < 1) { + return new String[0]; + } + + result = new String[count]; + System.arraycopy(from, start, result, 0, count); + + return result; + } + + // copyFrom() + // ------------------------------------------------------------------------- + + /** + * Returns the portion of the given string that comes before the last + * occurance of the specified separator. <br> + * If the separator could not be found in the given string, then the + * string is returned unchanged. + * + * <p> + * Examples: + * </p> + * + * <p> + * cutTail( "A/B/C", "/" ) ; // returns "A/B" <br> + * cutTail( "A/B/C", "," ) ; // returns "A/B/C" + * </p> + * + * <p></p> + * + * @param text The string from which to cut off the tail + * @param separator The separator from where to cut off + * + * @return the string without the separator and without the characters + * after the separator + * + * @see #prefix( String, String ) + * @see #suffix( String, String ) + * @see #cutHead( String, String ) + * @see #startingFrom( String, String ) + * @see #upTo( String, String ) + */ + public String cutTail(String text, String separator) { + int index; + + if ((text == null) || (separator == null)) { + return text; + } + + index = text.lastIndexOf(separator); + + if (index < 0) { + return text; + } + + return text.substring(0, index); + } + + // cutTail() + // ------------------------------------------------------------------------ + + /** + * Returns the portion of the given string that stands after the last + * occurance of the specified separator. <br> + * If the separator could not be found in the given string, then the + * string is returned unchanged. + * + * <p> + * Examples: + * </p> + * + * <p> + * cutHead( "A/B/C", "/" ) ; // returns "C" <br> + * cutHead( "A/B/C", "," ) ; // returns "A/B/C" + * </p> + * + * <p></p> + * + * @param text The string from which to cut off the head + * @param separator The separator up to which to cut off + * + * @return the string without the separator and without the characters + * before the separator + * + * @see #prefix( String, String ) + * @see #cutTail( String, String ) + * @see #suffix( String, String ) + * @see #startingFrom( String, String ) + * @see #upTo( String, String ) + */ + public String cutHead(String text, String separator) { + int index; + + if ((text == null) || (separator == null)) { + return text; + } + + index = text.lastIndexOf(separator); + + if (index < 0) { + return text; + } + + return text.substring(index + 1); + } + + // cutHead() + // ------------------------------------------------------------------------ + + /** + * Returns a string array with two elements where the first is the + * attribute name and the second is the attribute value. Splits the given + * string at the first occurance of separator and returns the piece before + * the separator in element 0 and the piece after the separator in the + * returned array. If the separator is not found, the first element + * contains the full string and the second an empty string. + * + * @param str The string that contains the name-value pair + * @param separator The separator between name and value + * + * @return + */ + public String[] splitNameValue(String str, String separator) { + String[] result = { "", "" }; + int index; + + if (str != null) { + index = str.indexOf(separator); + + if (index > 0) { + result[0] = str.substring(0, index); + result[1] = str.substring(index + separator.length()); + } else { + result[0] = str; + } + } + + return result; + } + + // splitNameValue() + // ------------------------------------------------------------------------- + + /** + * Returns the substring of the given string that comes before the first + * occurance of the specified separator. If the string starts with a + * separator, the result will be an empty string. If the string doesn't + * contain the separator the method returns null. + * + * <p> + * Examples: + * </p> + * + * <p> + * prefix( "A/B/C", "/" ) ; // returns "A" <br> + * prefix( "A/B/C", "," ) ; // returns null + * </p> + * + * <p></p> + * + * @param str The string of which the prefix is desired + * @param separator Separates the prefix from the rest of the string + * + * @return + * + * @see #suffix( String, String ) + * @see #cutTail( String, String ) + * @see #cutHead( String, String ) + * @see #startingFrom( String, String ) + * @see #upTo( String, String ) + */ + public String prefix(String str, String separator) { + return this.prefix(str, separator, true); + } + + // prefix() + // ------------------------------------------------------------------------- + + /** + * Returns the substring of the given string that comes after the first + * occurance of the specified separator. If the string ends with a + * separator, the result will be an empty string. If the string doesn't + * contain the separator the method returns null. + * + * <p> + * Examples: + * </p> + * + * <p> + * suffix( "A/B/C", "/" ) ; // returns "B/C" <br> + * suffix( "A/B/C", "," ) ; // returns null + * </p> + * + * <p></p> + * + * @param str The string of which the suffix is desired + * @param separator Separates the suffix from the rest of the string + * + * @return + * + * @see #prefix( String, String ) + * @see #cutTail( String, String ) + * @see #cutHead( String, String ) + * @see #startingFrom( String, String ) + * @see #upTo( String, String ) + */ + public String suffix(String str, String separator) { + return this.suffix(str, separator, true); + } + + // suffix() + // ------------------------------------------------------------------------- + + /** + * Returns the substring of the given string that comes before the first + * occurance of the specified separator. If the string starts with a + * separator, the result will be an empty string. If the string doesn't + * contain the separator the method returns the whole string unchanged. + * + * <p> + * Examples: + * </p> + * + * <p> + * upTo( "A/B/C", "/" ) ; // returns "A" <br> + * upTo( "A/B/C", "," ) ; // returns "A/B/C" <br> + * upTo( "/A/B/C", "/" ) ; // returns "" + * </p> + * + * <p></p> + * + * @param str The string of which the prefix is desired + * @param separator Separates the prefix from the rest of the string + * + * @return + * + * @see #prefix( String, String ) + * @see #cutTail( String, String ) + * @see #cutHead( String, String ) + * @see #startingFrom( String, String ) + * @see #suffix( String, String ) + */ + public String upTo(String str, String separator) { + return this.prefix(str, separator, false); + } + + // upTo() + // ------------------------------------------------------------------------- + + /** + * Returns the substring of the given string that comes after the first + * occurance of the specified separator. If the string doesn't contain the + * separator the method returns the whole string unchanged. + * + * <p> + * Examples: + * </p> + * + * <p> + * startingFrom( "A/B/C", "/" ) ; // returns "B/C" <br> + * startingFrom( "A/B/C", "," ) ; // returns "A/B/C" + * </p> + * + * <p></p> + * + * @param str The string of which the suffix is desired + * @param separator Separates the suffix from the rest of the string + * + * @return + * + * @see #prefix( String, String ) + * @see #cutTail( String, String ) + * @see #cutHead( String, String ) + * @see #suffix( String, String ) + * @see #upTo( String, String ) + */ + public String startingFrom(String str, String separator) { + return this.suffix(str, separator, false); + } + + // startingFrom() + // ------------------------------------------------------------------------- + + /** + * Returns a string that contains all characters of the given string in + * reverse order. + * + * @param str + * + * @return + */ + public String reverse(String str) { + if (str == null) { + return null; + } + + char[] newStr = new char[str.length()]; + StringCharacterIterator iterator = new StringCharacterIterator(str); + int i = 0; + + for (char ch = iterator.last(); ch != CharacterIterator.DONE; + ch = iterator.previous()) { + newStr[i] = ch; + i++; + } + + return new String(newStr); + } + + // reverse() + // ------------------------------------------------------------------------- + + /** + * Returns the given map with new entries from the specified String. If the + * specified map is null a new empty java.util.Hashtable will be created. <br> + * The string is split up into elements separated by the elementSeparator + * parameter. If this parameter is null the default separator "," is used. <br> + * After that each part is split up to a key-value pair separated by the + * keyValueSeparator parameter. If this parameter is null the default "=" + * is used. <br> + * Then the key-value pairs are added to the map and the map is returned. + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string that contains the list of key-value pairs + * @param elementSeparator The separator between the elements of the list + * @param keyValueSeparator The separator between the keys and values + * @param map The map to which the key-value pairs are added + * + * @return + */ + public Map toMap(String str, String elementSeparator, + String keyValueSeparator, Map map) { + Map result; + String elemSep; + String kvSep; + String[] assignments; + String[] nameValue; + + if (str == null) { + return map; + } + + result = ((map == null) ? new Hashtable() : map); + elemSep = (elementSeparator == null) ? "," : elementSeparator; + kvSep = (keyValueSeparator == null) ? "=" : keyValueSeparator; + assignments = this.parts(str, elemSep); + + for (int i = 0; i < assignments.length; i++) { + nameValue = this.splitNameValue(assignments[i], kvSep); + nameValue[0] = nameValue[0].trim(); + nameValue[1] = nameValue[1].trim(); + + if (nameValue[0].length() > 0) { + result.put(nameValue[0], nameValue[1]); + } + } + + return result; + } + + // asMap() + // ------------------------------------------------------------------------- + + /** + * Returns a new map object that contains all key-value pairs of the + * specified string. <br> + * The separator between the elements is assumed to be "," and "=" between + * key and value. + * + * <p> + * Example:<br> "main=Fred,support1=John,support2=Stella,manager=Oscar" + * </p> + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string with the list of key-value pairs + * + * @return + */ + public Map asMap(String str) { + return this.toMap(str, null, null, null); + } + + // asMap() + // ------------------------------------------------------------------------- + + /** + * Returns a new map object that contains all key-value pairs of the + * specified string. <br> + * The separator between the keys and values is assumed to be "=". + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string that contains the list of key-value pairs + * @param elementSeparator The separator between the elements of the list + * + * @return + */ + public Map asMap(String str, String elementSeparator) { + return this.toMap(str, elementSeparator, null, null); + } + + // asMap() + // ------------------------------------------------------------------------- + + /** + * Returns a new map object that contains all key-value pairs of the + * specified string. + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string that contains the list of key-value pairs + * @param elementSeparator The separator between the elements of the list + * @param keyValueSeparator The separator between the keys and values + * + * @return + */ + public Map asMap(String str, String elementSeparator, + String keyValueSeparator) { + return this.toMap(str, elementSeparator, keyValueSeparator, null); + } + + // asMap() + // ------------------------------------------------------------------------- + + /** + * Returns the given map object with all key-value pairs of the specified + * string added to it. <br> + * The separator between the keys and values is assumed to be "=". + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string that contains the list of key-value pairs + * @param elementSeparator The separator between the elements of the list + * @param map The map to which the key-value pairs are added + * + * @return + */ + public Map toMap(String str, String elementSeparator, Map map) { + return this.toMap(str, elementSeparator, null, map); + } + + // toMap() + // ------------------------------------------------------------------------- + + /** + * Adds all key-value pairs of the given string to the specified map. <br> + * The separator between the elements is assumed to be "," and "=" between + * key and value. + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string that contains the list of key-value pairs + * @param map The map to which the key-value pairs are added + * + * @return + */ + public Map toMap(String str, Map map) { + return this.toMap(str, null, null, map); + } + + // toMap() + // ------------------------------------------------------------------------- + + /** + * Adds all key-value pairs of the given string to a new properties object. <br> + * The separator between the elements is assumed to be "," and "=" between + * key and value. + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string that contains the list of key-value pairs + * + * @return + */ + public Properties asProperties(String str) { + return this.toProperties(str, null); + } + + // asProperties() + // ------------------------------------------------------------------------- + + /** + * Adds all key-value pairs of the given string to the specified + * properties. <br> + * The separator between the elements is assumed to be "," and "=" between + * key and value. + * + * <p> + * <b>Be aware that all leading and trailing whitespaces of keys and values + * will be removed!</b> + * </p> + * + * @param str The string that contains the list of key-value pairs + * @param properties The properties where the key-value pairs should be + * added + * + * @return + */ + public Properties toProperties(String str, Properties properties) { + Properties props = (properties == null) ? new Properties() : properties; + + return (Properties) this.toMap(str, null, null, props); + } + + // toProperties() + // ------------------------------------------------------------------------- + // ========================================================================= + // PROTECTED INSTANCE METHODS + // ========================================================================= + + /** + * Cuts off all leading and trailing occurences of separator in text. + * + * @param text + * @param separator + * + * @return + */ + protected String trimSeparator(String text, String separator) { + int sepLen = separator.length(); + + while (text.startsWith(separator)) { + text = text.substring(separator.length()); + } + + while (text.endsWith(separator)) { + text = text.substring(0, text.length() - sepLen); + } + + return text; + } + + // trimSeparator() + // ------------------------------------------------------------------------- + + /** + * Returns an array of substrings of the given text. <br> + * The separators between the substrings are the given delimiters. Each + * character in the delimiter string is treated as a separator. + * + * @param text The string that should be splitted into substrings + * @param delimiters All characters that should be recognized as a + * separator or substrings + * @param all If true, empty elements will be returned, otherwise thye are + * skipped + * + * @return An array of substrings of the given text + */ + protected String[] parts(String text, String delimiters, boolean all) { + ArrayList result = null; + StringTokenizer tokenizer = null; + + if (text == null) { + return null; + } + + if ((delimiters == null) || (delimiters.length() == 0)) { + String[] resultArray = { text }; + + return resultArray; + } + + if (text.length() == 0) { + return new String[0]; + } else { + result = new ArrayList(); + tokenizer = new StringTokenizer(text, delimiters, all); + + if (all) { + this.collectParts(result, tokenizer, delimiters); + } else { + this.collectParts(result, tokenizer); + } + } + + return (String[]) result.toArray(new String[0]); + } + + // parts() + // ------------------------------------------------------------------------- + protected void collectParts(List list, StringTokenizer tokenizer) { + while (tokenizer.hasMoreTokens()) { + list.add(tokenizer.nextToken()); + } + } + + // collectParts() + // ------------------------------------------------------------------------- + protected void collectParts(List list, StringTokenizer tokenizer, + String delimiter) { + String token; + boolean lastWasDelimiter = false; + + while (tokenizer.hasMoreTokens()) { + token = tokenizer.nextToken(); + + if (delimiter.indexOf(token) >= 0) { + if (lastWasDelimiter) { + list.add(""); + } + + lastWasDelimiter = true; + } else { + list.add(token); + lastWasDelimiter = false; + } + } + } + + // collectParts() + // ------------------------------------------------------------------------- + + /** + * Returns the given text split up into an array of strings, at the + * occurrances of the separator string. In contrary to method parts() the + * separator is a one or many character sequence delimiter. That is, only + * the exact sequence of the characters in separator identifies the end + * of a substring. Parameter all defines whether empty strings between + * consecutive separators are added to the result or not. + * + * @param text The text to be split up + * @param separator The string that separates the substrings + * @param all If true, empty strings are added, otherwise skipped + * + * @return An array of substrings not containing any separator anymore + * + * @see #parts(String, String, boolean) + */ + protected String[] substrings(String text, String separator, boolean all) { + int index = 0; + int start = 0; + int sepLen = 0; + int strLen = 0; + String str = text; + ArrayList strings = new ArrayList(); + + if (text == null) { + return new String[0]; + } + + if ((separator == null) || (separator.length() == 0)) { + if (text.length() == 0) { + return new String[0]; + } + + String[] resultArray = { text }; + + return resultArray; + } + + if (!all) { + str = this.trimSeparator(text, separator); + } + + strLen = str.length(); + + if (strLen > 0) { + sepLen = separator.length(); + index = str.indexOf(separator, start); + + while (index >= 0) { + if (all) { + if (index > 0) { + strings.add(str.substring(start, index)); + } + } else { + if (index > (start + sepLen)) { + strings.add(str.substring(start, index)); + } + } + + start = index + sepLen; + index = str.indexOf(separator, start); + } + + if (start < strLen) { + strings.add(str.substring(start)); + } + } + + return (String[]) strings.toArray(new String[0]); + } + + // substrings() + // ------------------------------------------------------------------------- + protected String padCh(String str, int len, char ch, boolean left) { + StringBuffer buffer = null; + int missing = len - str.length(); + + if (missing <= 0) { + return str; + } + + buffer = new StringBuffer(len); + + if (!left) { + buffer.append(str); + } + + for (int i = 1; i <= missing; i++) { + buffer.append(ch); + } + + if (left) { + buffer.append(str); + } + + return buffer.toString(); + } + + // padCh() + // ------------------------------------------------------------------------- + protected int indexOfString(String[] strArray, String searchStr, + boolean ignoreCase) { + if ((strArray == null) || (strArray.length == 0)) { + return -1; + } + + boolean found = false; + + for (int i = 0; i < strArray.length; i++) { + if (strArray[i] == null) { + if (searchStr == null) { + found = true; + } + } else { + if (ignoreCase) { + found = strArray[i].equalsIgnoreCase(searchStr); + } else { + found = strArray[i].equals(searchStr); + } + } + + if (found) { + return i; + } + } + + return -1; + } + + // indexOfString() + // ------------------------------------------------------------------------- + + /** + * Returns the substring of the given string that comes before the first + * occurance of the specified separator. If the string starts with a + * separator, the result will be an empty string. If the string doesn't + * contain the separator the method returns null or the whole string, + * depending on the returnNull flag. + * + * @param str The string of which the prefix is desired + * @param separator Separates the prefix from the rest of the string + * @param returnNull Specifies if null will be returned if no separator is + * found + * + * @return + */ + protected String prefix(String str, String separator, boolean returnNull) { + if (str == null) { + return null; + } + + if (separator == null) { + return (returnNull ? null : str); + } + + int index = str.indexOf(separator); + + if (index >= 0) { + return str.substring(0, index); + } else { + return (returnNull ? null : str); + } + } + + // prefix() + // ------------------------------------------------------------------------- + + /** + * Returns the substring of the given string that comes after the first + * occurance of the specified separator. If the string ends with a + * separator, the result will be an empty string. If the string doesn't + * contain the separator the method returns null or the whole string, + * depending on the returnNull flag. + * + * @param str The string of which the suffix is desired + * @param separator Separates the suffix from the rest of the string + * @param returnNull Specifies if null will be returned if no separator is + * found + * + * @return + */ + protected String suffix(String str, String separator, boolean returnNull) { + if (str == null) { + return null; + } + + if (separator == null) { + return (returnNull ? null : str); + } + + int index = str.indexOf(separator); + + if (index >= 0) { + return str.substring(index + separator.length()); + } else { + return (returnNull ? null : str); + } + } + + // suffix() + // ------------------------------------------------------------------------- + + /** + * Removes the given strings from the array. If removeStrings is null it + * means that all null values are removed from the first array. + * + * @param strings + * @param removeStrings + * + * @return + */ + protected String[] removeFromStringArray(String[] strings, + String[] removeStrings) { + List list; + boolean remains; + list = new ArrayList(strings.length); + + for (int i = 0; i < strings.length; i++) { + if (removeStrings == null) { + remains = strings[i] != null; + } else { + remains = !this.contains(removeStrings, strings[i]); + } + + if (remains) { + list.add(strings[i]); + } + } + + return (String[]) list.toArray(new String[list.size()]); + } + + // removeFromStringArray() + // ------------------------------------------------------------------------- +} + + +// class StringUtil diff --git a/src/com/sshtools/daemon/vfs/VFSMount.java b/src/com/sshtools/daemon/vfs/VFSMount.java new file mode 100644 index 0000000000000000000000000000000000000000..ffcad6bf9c8444acf934d34df8084e0fc1659e80 --- /dev/null +++ b/src/com/sshtools/daemon/vfs/VFSMount.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.vfs; + +import com.sshtools.j2ssh.configuration.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class VFSMount { + private String mount; + private String path; + private HashMap acl = new HashMap(); + private boolean isroot = false; + + /** + * Creates a new VFSMount object. + * + * @param mount + * @param path + * + * @throws IOException + */ + public VFSMount(String mount, String path) throws IOException { + path.replace('\\', '/'); + + // Replace any tokens + int index = path.indexOf("%HOME%"); + + if (index >= 0) { + path = ((index > 0) ? path.substring(0, index) : "") + + ConfigurationLoader.getHomeDirectory() + + (((index + 6) < (path.length() - 1)) + ? path.substring(index + + ((path.charAt(index + 6) == '/') ? 7 : 6)) : ""); + } + + File f = new File(path); + + if (!f.exists()) { + f.mkdirs(); + } + + if (!mount.trim().startsWith("/")) { + this.mount = "/" + mount.trim(); + } else { + this.mount = mount.trim(); + } + + this.path = f.getCanonicalPath(); + } + + /** + * + * + * @return + */ + public boolean isRoot() { + return isroot; + } + + /** + * + * + * @param isroot + */ + public void setRoot(boolean isroot) { + this.isroot = isroot; + } + + /** + * + * + * @param permissions + */ + public void setPermissions(VFSPermission permissions) { + acl.put(permissions.getName(), permissions); + } + + /** + * + * + * @return + */ + public String getPath() { + return path.replace('\\', '/'); + } + + /** + * + * + * @return + */ + public String getMount() { + return mount; + } + + /** + * + * + * @return + */ + public Map getPermissions() { + return acl; + } +} diff --git a/src/com/sshtools/daemon/vfs/VFSPermission.java b/src/com/sshtools/daemon/vfs/VFSPermission.java new file mode 100644 index 0000000000000000000000000000000000000000..04a99c57c4e63efa7c8af866bbf0dfa28c911f48 --- /dev/null +++ b/src/com/sshtools/daemon/vfs/VFSPermission.java @@ -0,0 +1,162 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.vfs; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class VFSPermission { + private boolean canRead; + private boolean canWrite; + private boolean canExecute; + private String name; + + /** + * Creates a new VFSPermission object. + * + * @param name + * @param permissions + */ + public VFSPermission(String name, String permissions) { + this.name = name; + setPermissions(permissions); + } + + /** + * Creates a new VFSPermission object. + * + * @param name + */ + public VFSPermission(String name) { + this.name = name; + setPermissions("rwx"); + } + + /** + * + * + * @return + */ + public String getName() { + return name; + } + + /** + * + * + * @param permissions + */ + public void setPermissions(String permissions) { + canRead = false; + canWrite = false; + canExecute = false; + + for (int i = 0; i < permissions.length(); i++) { + switch (permissions.charAt(i)) { + case 'r': { + canRead = true; + + break; + } + + case 'w': { + canWrite = true; + + break; + } + + case 'x': { + canExecute = true; + + break; + } + } + } + } + + /** + * + * + * @return + */ + public String getPermissions() { + return (canRead ? "r" : "") + (canWrite ? "w" : "") + + (canExecute ? "x" : ""); + } + + /** + * + * + * @param permissions + * + * @return + */ + public boolean verifyPermissions(String permissions) { + String tmp = getPermissions(); + String ch; + + for (int i = 0; i < permissions.length(); i++) { + ch = permissions.substring(i, 1); + + if (tmp.indexOf(ch) == -1) { + return false; + } + } + + return true; + } + + /** + * + * + * @return + */ + public boolean canRead() { + return canRead; + } + + /** + * + * + * @return + */ + public boolean canWrite() { + return canWrite; + } + + /** + * + * + * @return + */ + public boolean canExecute() { + return canExecute; + } +} diff --git a/src/com/sshtools/daemon/vfs/VFSPermissionHandler.java b/src/com/sshtools/daemon/vfs/VFSPermissionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..be5e4f0ce51f838282644fcbab6fab1e01966226 --- /dev/null +++ b/src/com/sshtools/daemon/vfs/VFSPermissionHandler.java @@ -0,0 +1,48 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.vfs; + +import com.sshtools.daemon.platform.*; + +/** + * <p>Title: </p> + * <p>Description: </p> + * <p>Copyright: Copyright (c) 2003</p> + * <p>Company: </p> + * @author Lee David Painter + * @version $Id: VFSPermissionHandler.java,v 1.6 2003/09/11 15:37:07 martianx Exp $ + */ +import java.io.*; + + +public interface VFSPermissionHandler { + public void verifyPermissions(String username, String path, + String permissions) + throws PermissionDeniedException, FileNotFoundException, IOException; + + public String getVFSHomeDirectory(String username) + throws FileNotFoundException; +} diff --git a/src/com/sshtools/daemon/vfs/VirtualFileSystem.java b/src/com/sshtools/daemon/vfs/VirtualFileSystem.java new file mode 100644 index 0000000000000000000000000000000000000000..a152e7871002624d52a6f4763d5a6aa5eaa00fcc --- /dev/null +++ b/src/com/sshtools/daemon/vfs/VirtualFileSystem.java @@ -0,0 +1,1168 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.daemon.vfs; + +import com.sshtools.daemon.configuration.PlatformConfiguration; +import com.sshtools.daemon.platform.InvalidHandleException; +import com.sshtools.daemon.platform.NativeAuthenticationProvider; +import com.sshtools.daemon.platform.NativeFileSystemProvider; +import com.sshtools.daemon.platform.PermissionDeniedException; +import com.sshtools.daemon.platform.UnsupportedFileOperationException; + +import com.sshtools.j2ssh.SshThread; +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.io.UnsignedInteger64; +import com.sshtools.j2ssh.sftp.FileAttributes; +import com.sshtools.j2ssh.sftp.SftpFile; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.EOFException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.32 $ + */ +public class VirtualFileSystem extends NativeFileSystemProvider { + private static String USER_HOME = "/home/"; + private static Map vfsmounts; + private static VFSMount vfsroot; + private static Log log = LogFactory.getLog(VirtualFileSystem.class); + private static VFSPermissionHandler permissionHandler = null; + + static { + try { + vfsmounts = ((PlatformConfiguration) ConfigurationLoader.getConfiguration(PlatformConfiguration.class)).getVFSMounts(); + vfsroot = ((PlatformConfiguration) ConfigurationLoader.getConfiguration(PlatformConfiguration.class)).getVFSRoot(); + } catch (ConfigurationException ex) { + log.error("Failed to initialize the Virtual File System", ex); + } + } + + private Map openFiles = new HashMap(); + + /** + * Creates a new VirtualFileSystem object. + * + * @throws IOException + */ + public VirtualFileSystem() throws IOException { + if (!ConfigurationLoader.isConfigurationAvailable( + PlatformConfiguration.class)) { + throw new IOException("No valid platform configuration available"); + } + } + + public static void setPermissionHandler( + VFSPermissionHandler permissionHandler) { + VirtualFileSystem.permissionHandler = permissionHandler; + } + + private static String getVFSHomeDirectory(String username) + throws FileNotFoundException { + if (permissionHandler != null) { + return permissionHandler.getVFSHomeDirectory(username); + } else { + return USER_HOME + username; + } + } + + private static String getNFSHomeDirectory() throws FileNotFoundException { + try { + if (Thread.currentThread() instanceof SshThread && + SshThread.hasUserContext()) { + NativeAuthenticationProvider nap = NativeAuthenticationProvider.getInstance(); + + return nap.getHomeDirectory(SshThread.getCurrentThreadUser()); + } else { + throw new FileNotFoundException("There is no user logged in"); + } + } catch (IOException e) { + throw new FileNotFoundException(e.getMessage()); + } + } + + /** + * + * + * @param str + * @param with + * + * @return + */ + public static boolean startsWithIgnoreCase(String str, String with) { + return str.substring(0, + (with.length() > str.length()) ? str.length() : with.length()) + .equalsIgnoreCase(with); + } + + /** + * + * + * @param nfspath + * + * @return + * + * @throws FileNotFoundException + */ + public static String translateNFSPath(String nfspath) + throws FileNotFoundException { + nfspath = nfspath.replace('\\', '/'); + + // ./ means home + if (nfspath.startsWith("./")) { + nfspath = nfspath.substring(2); + } + + //if (startsWithIgnoreCase(nfspath, nfshome)) { + try { + String nfshome = getNFSHomeDirectory().replace('\\', '/'); + nfshome = translateCanonicalPath(nfshome, nfshome); + + String vfshome = getVFSHomeDirectory(SshThread.getCurrentThreadUser()); + + // First check for the userhome + log.debug("NFSPath=" + nfspath); + log.debug("NFSHome=" + nfshome); + nfspath = translateCanonicalPath(nfspath, nfshome); + + int idx = nfspath.indexOf(nfshome); + + return vfshome + nfspath.substring(nfshome.length()); + + // StringBuffer buf = new StringBuffer(nfspath); + // buf = buf.replace(idx, idx + nfshome.length(), vfshome); + // return buf.toString(); /*nfspath.replaceFirst(nfshome, vfshome);*/ + //} + } catch (FileNotFoundException ex) { /* Ignore as we will try other mounts */ + } + + // Now lets translate from the available mounts + Iterator it = vfsmounts.entrySet().iterator(); + Map.Entry entry; + String mount; + String path; + VFSMount m; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + mount = (String) entry.getKey(); + m = (VFSMount) entry.getValue(); + path = m.getPath(); + log.debug(m.getMount() + "=" + m.getPath()); + + // if (startsWithIgnoreCase(nfspath, path)) { + try { + nfspath = translateCanonicalPath(nfspath, path); + + int idx = nfspath.indexOf(path); + StringBuffer buf = new StringBuffer(nfspath); + buf = buf.replace(idx, idx + path.length(), mount); + + return buf.toString(); + } catch (FileNotFoundException ex) { + /* Ingore as we will try other mounts */ + } + + // } + } + + // if (startsWithIgnoreCase(nfspath, vfsroot.getPath())) { + log.debug("VFSRoot=" + vfsroot.getPath()); + nfspath = translateCanonicalPath(nfspath, vfsroot.getPath()); + path = nfspath.substring(vfsroot.getPath().length()); //replaceFirst(vfsroot.getPath(), ""); + + return (path.startsWith("/") ? path : ("/" + path)); + + // } else { + // throw new FileNotFoundException(nfspath + " could not be found"); + // } + } + + private static VFSMount getMount(String vfspath) + throws FileNotFoundException, IOException { + String vfshome = getVFSHomeDirectory(SshThread.getCurrentThreadUser()); + VFSMount m; + + if (vfspath.startsWith("/")) { + if (vfspath.startsWith(vfshome)) { + m = new VFSMount(vfshome, getNFSHomeDirectory()); + m.setPermissions(new VFSPermission( + SshThread.getCurrentThreadUser(), "rwx")); + + return m; + } else { + Iterator it = vfsmounts.entrySet().iterator(); + Map.Entry entry; + String mount; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + mount = (String) entry.getKey(); + + if (vfspath.startsWith(mount)) { + return (VFSMount) entry.getValue(); + } + } + + if (vfsroot != null) { + return vfsroot; + } else { + throw new FileNotFoundException("The path was not found"); + } + } + } else { + m = new VFSMount(vfshome, getNFSHomeDirectory()); + m.setPermissions(new VFSPermission(vfshome.substring( + vfshome.lastIndexOf("/")), "rwx")); + + return m; + } + } + + /** + * + * + * @param vfspath + * + * @return + * + * @throws FileNotFoundException + */ + public static String translateVFSPath(String vfspath) + throws FileNotFoundException { + return translateVFSPath(vfspath, null); + } + + public static String translateVFSPath(String vfspath, String vfscwd) + throws FileNotFoundException { + // Translate any backslashes for sanity + vfspath = vfspath.replace('\\', '/').trim(); + + try { + if (!vfspath.startsWith("/")) { + // Work out the path using the current directory + String path = (((vfscwd == null) || vfscwd.trim().equals("")) + ? getVFSHomeDirectory(SshThread.getCurrentThreadUser()) + : vfscwd); + vfspath = path + (path.endsWith("/") ? "" : "/") + vfspath; + } + + String nfshome = getNFSHomeDirectory().replace('\\', '/'); + String vfshome = getVFSHomeDirectory(SshThread.getCurrentThreadUser()); + + if (vfspath.startsWith(vfshome)) { + // Return the canonicalized system dependent path + if (vfspath.length() > vfshome.length()) { + return translateCanonicalPath(nfshome + + (nfshome.endsWith("/") ? "" : "/") + + vfspath.substring(vfshome.length() + 1), nfshome); + } else { + return translateCanonicalPath(nfshome, nfshome); + } + } + } catch (FileNotFoundException ex) { + // Ignore since we dont always need to be running as a user + } + + // The path does not refer to the absolute USER_HOME + // so we will look up using the platform.xml VFS mounts + Iterator it = vfsmounts.entrySet().iterator(); + Map.Entry entry; + String mount; + String path; + VFSMount m; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + mount = (String) entry.getKey(); + m = (VFSMount) entry.getValue(); + path = m.getPath(); + + if (vfspath.startsWith(mount)) { + // Lets translate the path, making sure we do not move outside + // vfs with .. + String str = path + vfspath.substring(mount.length()); + + // vfspath.replaceFirst(mount, + // path) + return translateCanonicalPath(str, path); + } + } + + // If we reached here then the VFS path did not refer to an optional mount + // or the users home directory, so lets attempt to use the VFS root is there + // is one defined + if (vfsroot != null) { + path = vfsroot.getPath() + + (vfsroot.getPath().endsWith("/") ? vfspath.substring(1) : vfspath); + + return translateCanonicalPath(path, vfsroot.getPath()); + } else { + throw new FileNotFoundException("The file could not be found"); + } + } + + /*else { + try { + String nfshome = (nfscwd == null || nfscwd.trim().equals("") ? getNFSHomeDirectory() : nfscwd); + String path = nfshome + (nfshome.endsWith("/") ? "" : "/") + vfspath; + return translateCanonicalPath(path, nfshome); + } + catch (FileNotFoundException ex1) { + throw new FileNotFoundException( + "Only fully qualified VFS paths can be translated outside of a user context"); + } + /* String path = nfshome + (nfshome.endsWith("/") ? "" : "/") + + vfspath; + return translateCanonicalPath(path, nfshome);*/ + + //} + + /** + * + * + * @param path + * @param securemount + * + * @return + * + * @throws FileNotFoundException + */ + public static String translateCanonicalPath(String path, String securemount) + throws FileNotFoundException { + try { + log.debug("Translating for canonical path " + path + + " against secure mount " + securemount); + + File f = new File(path); + String canonical = f.getCanonicalPath().replace('\\', '/'); + File f2 = new File(securemount); + String canonical2 = f2.getCanonicalPath().replace('\\', '/'); + + // Verify that the canonical path does not exit out of the mount + if (canonical.startsWith(canonical2)) { + return canonical; + } else { + throw new FileNotFoundException(path + " could not be found"); + } + } catch (IOException ex) { + throw new FileNotFoundException(path + " could not be found"); + } + } + + /** + * + * + * @param path + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public boolean makeDirectory(String path) + throws PermissionDeniedException, FileNotFoundException, IOException { + // String realPath = path; + path = VirtualFileSystem.translateVFSPath(path); + + File f = new File(path); + verifyPermissions(SshThread.getCurrentThreadUser(), path, "rw"); + log.debug("Creating directory " + f.getAbsolutePath()); + + return f.mkdir(); + } + + /** + * + * + * @param path + * + * @return + * + * @throws FileNotFoundException + * @throws IOException + */ + public VFSPermission getVFSPermission(String path) + throws FileNotFoundException, IOException { + VFSMount mount = getMount(translateNFSPath(path)); + + if (mount.getPermissions().containsKey(SshThread.getCurrentThreadUser())) { + return (VFSPermission) mount.getPermissions().get(SshThread.getCurrentThreadUser()); + } else { + return (VFSPermission) mount.getPermissions().get("default"); + } + } + + /** + * + * + * @param handle + * + * @return + * + * @throws IOException + * @throws InvalidHandleException + */ + public FileAttributes getFileAttributes(byte[] handle) + throws IOException, InvalidHandleException { + String shandle = new String(handle); + + if (openFiles.containsKey(shandle)) { + Object obj = openFiles.get(shandle); + File f; + + if (obj instanceof OpenFile) { + f = ((OpenFile) obj).getFile(); + } else if (obj instanceof OpenDirectory) { + f = ((OpenDirectory) obj).getFile(); + } else { + throw new IOException("Unexpected open file handle"); + } + + VFSPermission permissions = getVFSPermission(f.getAbsolutePath()); + + if (permissions == null) { + throw new IOException("No default permissions set"); + } + + FileAttributes attrs = new FileAttributes(); + attrs.setSize(new UnsignedInteger64(String.valueOf(f.length()))); + attrs.setTimes(new UnsignedInteger32(f.lastModified() / 1000), + new UnsignedInteger32(f.lastModified() / 1000)); + + boolean canExec = true; + + try { + if (System.getSecurityManager() != null) { + System.getSecurityManager().checkExec(f.getCanonicalPath()); + } + } catch (SecurityException ex1) { + canExec = false; + } + + attrs.setPermissions((((f.canRead() && permissions.canRead()) ? "r" + : "-") + + ((f.canWrite() && permissions.canWrite()) ? "w" : "-") + + ((canExec && permissions.canExecute()) ? "x" : "-"))); + attrs.setPermissions(new UnsignedInteger32(attrs.getPermissions() + .longValue() | + (f.isDirectory() ? FileAttributes.S_IFDIR + : FileAttributes.S_IFREG))); + + return attrs; + } else { + throw new InvalidHandleException("The handle is invalid"); + } + } + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + * @throws FileNotFoundException + */ + public FileAttributes getFileAttributes(String path) + throws IOException, FileNotFoundException { + log.debug("Getting file attributes for " + path); + path = translateVFSPath(path); + + // Look up the VFS mount attributes + File f = new File(path); + path = f.getCanonicalPath(); + + if (!f.exists()) { + throw new FileNotFoundException(path + " doesn't exist"); + } + + VFSPermission permissions = getVFSPermission(path); + + if (permissions == null) { + throw new IOException("No default permissions set"); + } + + FileAttributes attrs = new FileAttributes(); + attrs.setSize(new UnsignedInteger64(String.valueOf(f.length()))); + attrs.setTimes(new UnsignedInteger32(f.lastModified() / 1000), + new UnsignedInteger32(f.lastModified() / 1000)); + + boolean canExec = true; + + try { + if (System.getSecurityManager() != null) { + System.getSecurityManager().checkExec(f.getCanonicalPath()); + } + } catch (SecurityException ex1) { + canExec = false; + } + + attrs.setPermissions((((f.canRead() && permissions.canRead()) ? "r" : "-") + + ((f.canWrite() && permissions.canWrite()) ? "w" : "-") + + ((canExec && permissions.canExecute()) ? "x" : "-"))); + attrs.setPermissions(new UnsignedInteger32(attrs.getPermissions() + .longValue() | + (f.isDirectory() ? FileAttributes.S_IFDIR : FileAttributes.S_IFREG))); + + return attrs; + } + + /** + * + * + * @param path + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public byte[] openDirectory(String path) + throws PermissionDeniedException, FileNotFoundException, IOException { + String realPath = path; + path = VirtualFileSystem.translateVFSPath(path); + + File f = new File(path); + verifyPermissions(SshThread.getCurrentThreadUser(), path, "r"); + + if (f.exists()) { + if (f.isDirectory()) { + openFiles.put(f.toString(), new OpenDirectory(realPath, path, f)); + + return f.toString().getBytes("US-ASCII"); + } else { + throw new IOException(translateNFSPath(path) + + " is not a directory"); + } + } else { + throw new FileNotFoundException(translateNFSPath(path) + + " does not exist"); + } + } + + /** + * + * + * @param handle + * + * @return + * + * @throws InvalidHandleException + * @throws EOFException + * @throws IOException + */ + public SftpFile[] readDirectory(byte[] handle) + throws InvalidHandleException, EOFException, IOException { + String shandle = new String(handle); + + if (openFiles.containsKey(shandle)) { + Object obj = openFiles.get(shandle); + + if (obj instanceof OpenDirectory) { + OpenDirectory dir = (OpenDirectory) obj; + int pos = dir.getPosition(); + File[] children = dir.getChildren(); + + if (children == null) { + throw new IOException("Permission denined."); + } + + int count = ((children.length - pos) < 100) + ? (children.length - pos) : 100; + + if (count > 0) { + SftpFile[] files = new SftpFile[count]; + + for (int i = 0; i < files.length; i++) { + File f = children[pos + i]; + String absolutePath = dir.realPath + "/" + f.getName(); + SftpFile sftpfile = new SftpFile(f.getName(), + getFileAttributes(absolutePath)); + files[i] = sftpfile; + } + + dir.readpos = pos + files.length; + + return files; + } else { + throw new EOFException("There are no more files"); + } + } else { + throw new InvalidHandleException( + "Handle is not an open directory"); + } + } else { + throw new InvalidHandleException("The handle is invalid"); + } + } + + /** + * + * + * @param path + * @param flags + * @param attrs + * + * @return + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public byte[] openFile(String path, UnsignedInteger32 flags, + FileAttributes attrs) + throws PermissionDeniedException, FileNotFoundException, IOException { + path = VirtualFileSystem.translateVFSPath(path); + + File f = new File(path); + verifyPermissions(SshThread.getCurrentThreadUser(), path, "r"); + + // Check if the file does not exist and process according to flags + if (!f.exists()) { + if ((flags.intValue() & NativeFileSystemProvider.OPEN_CREATE) == NativeFileSystemProvider.OPEN_CREATE) { + // The file does not exist and the create flag is present so lets create it + if (!f.createNewFile()) { + throw new IOException(translateNFSPath(path) + + " could not be created"); + } + } else { + // The file does not exist and no create flag present + throw new FileNotFoundException(translateNFSPath(path) + + " does not exist"); + } + } else { + if (((flags.intValue() & NativeFileSystemProvider.OPEN_CREATE) == NativeFileSystemProvider.OPEN_CREATE) && + ((flags.intValue() & + NativeFileSystemProvider.OPEN_EXCLUSIVE) == NativeFileSystemProvider.OPEN_EXCLUSIVE)) { + // The file exists but the EXCL flag is set which requires that the + // file should not exist prior to creation, so throw a status exception + throw new IOException(translateNFSPath(path) + + " already exists"); + } + } + + // The file now exists so open the file according to the flags yb building the relevant + // flags for the RandomAccessFile class + String mode = "r" + + (((flags.intValue() & NativeFileSystemProvider.OPEN_WRITE) == NativeFileSystemProvider.OPEN_WRITE) + ? "ws" : ""); + RandomAccessFile raf = new RandomAccessFile(f, mode); + + // Determine whether we need to truncate the file + if (((flags.intValue() & NativeFileSystemProvider.OPEN_CREATE) == NativeFileSystemProvider.OPEN_CREATE) && + ((flags.intValue() & NativeFileSystemProvider.OPEN_TRUNCATE) == NativeFileSystemProvider.OPEN_TRUNCATE)) { + // Set the length to zero + raf.setLength(0); + } + + // Record the open file + openFiles.put(raf.toString(), new OpenFile(f, raf, flags)); + + // Return the handle + return raf.toString().getBytes("US-ASCII"); + } + + /** + * + * + * @param handle + * @param offset + * @param len + * + * @return + * + * @throws InvalidHandleException + * @throws EOFException + * @throws IOException + */ + public byte[] readFile(byte[] handle, UnsignedInteger64 offset, + UnsignedInteger32 len) + throws InvalidHandleException, EOFException, IOException { + String shandle = new String(handle); + + if (openFiles.containsKey(shandle)) { + Object obj = openFiles.get(shandle); + + if (obj instanceof OpenFile) { + OpenFile file = (OpenFile) obj; + + if ((file.getFlags().intValue() & + NativeFileSystemProvider.OPEN_READ) == NativeFileSystemProvider.OPEN_READ) { + byte[] buf = new byte[len.intValue()]; + + if (file.getRandomAccessFile().getFilePointer() != offset.longValue()) { + file.getRandomAccessFile().seek(offset.longValue()); + } + + int read = file.getRandomAccessFile().read(buf); + + if (read >= 0) { + if (read == buf.length) { + return buf; + } else { + byte[] tmp = new byte[read]; + System.arraycopy(buf, 0, tmp, 0, read); + + return tmp; + } + } else { + throw new EOFException("The file is EOF"); + } + } else { + throw new InvalidHandleException( + "The file handle was not opened for reading"); + } + } else { + throw new InvalidHandleException("Handle is not an open file"); + } + } else { + throw new InvalidHandleException("The handle is invalid"); + } + } + + /** + * + * + * @param handle + * @param offset + * @param data + * @param off + * @param len + * + * @throws InvalidHandleException + * @throws IOException + */ + public void writeFile(byte[] handle, UnsignedInteger64 offset, byte[] data, + int off, int len) throws InvalidHandleException, IOException { + String shandle = new String(handle); + + if (openFiles.containsKey(shandle)) { + Object obj = openFiles.get(shandle); + + if (obj instanceof OpenFile) { + OpenFile file = (OpenFile) obj; + + if ((file.getFlags().intValue() & + NativeFileSystemProvider.OPEN_WRITE) == NativeFileSystemProvider.OPEN_WRITE) { + if ((file.getFlags().intValue() & + NativeFileSystemProvider.OPEN_APPEND) == NativeFileSystemProvider.OPEN_APPEND) { + // Force the data to be written to the end of the file by seeking to the end + file.getRandomAccessFile().seek(file.getRandomAccessFile() + .length()); + } else if (file.getRandomAccessFile().getFilePointer() != offset.longValue()) { + // Move the file pointer if its not in the write place + file.getRandomAccessFile().seek(offset.longValue()); + } + + file.getRandomAccessFile().write(data, off, len); + } else { + throw new InvalidHandleException( + "The file was not opened for writing"); + } + } else { + throw new InvalidHandleException("Handle is not an open file"); + } + } else { + throw new InvalidHandleException("The handle is invalid"); + } + } + + /** + * + * + * @param handle + * + * @throws InvalidHandleException + * @throws IOException + */ + public void closeFile(byte[] handle) + throws InvalidHandleException, IOException { + String shandle = new String(handle); + + if (openFiles.containsKey(shandle)) { + Object obj = openFiles.get(shandle); + + if (obj instanceof OpenDirectory) { + openFiles.remove(shandle); + } else if (obj instanceof OpenFile) { + ((OpenFile) obj).getRandomAccessFile().close(); + openFiles.remove(shandle); + } else { + throw new InvalidHandleException("Internal server error"); + } + } else { + throw new InvalidHandleException("The handle is invalid"); + } + } + + /** + * + * + * @param path + * + * @throws PermissionDeniedException + * @throws IOException + * @throws FileNotFoundException + */ + public void removeFile(String path) + throws PermissionDeniedException, IOException, FileNotFoundException { + path = VirtualFileSystem.translateVFSPath(path); + + File f = new File(path); + + if (f.exists()) { + try { + if (f.isFile()) { + if (!f.delete()) { + throw new IOException("Failed to delete " + + translateNFSPath(path)); + } + } else { + throw new IOException(translateNFSPath(path) + + " is a directory, use remove directory command to remove"); + } + } catch (SecurityException se) { + throw new PermissionDeniedException("Permission denied"); + } + } else { + throw new FileNotFoundException(translateNFSPath(path) + + " does not exist"); + } + } + + /** + * + * + * @param oldpath + * @param newpath + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public void renameFile(String oldpath, String newpath) + throws PermissionDeniedException, FileNotFoundException, IOException { + oldpath = VirtualFileSystem.translateVFSPath(oldpath); + newpath = VirtualFileSystem.translateVFSPath(newpath); + + File f = new File(oldpath); + verifyPermissions(SshThread.getCurrentThreadUser(), oldpath, "rw"); + verifyPermissions(SshThread.getCurrentThreadUser(), newpath, "rw"); + + if (f.exists()) { + File f2 = new File(newpath); + + if (!f2.exists()) { + if (!f.renameTo(f2)) { + throw new IOException("Failed to rename file " + + translateNFSPath(oldpath)); + } + } else { + throw new IOException(translateNFSPath(newpath) + + " already exists"); + } + } else { + throw new FileNotFoundException(translateNFSPath(oldpath) + + " does not exist"); + } + } + + /** + * + * + * @param path + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public void removeDirectory(String path) + throws PermissionDeniedException, FileNotFoundException, IOException { + path = VirtualFileSystem.translateVFSPath(path); + + File f = new File(path); + verifyPermissions(SshThread.getCurrentThreadUser(), path, "rw"); + + if (f.isDirectory()) { + if (f.exists()) { + if (f.listFiles().length == 0) { + if (!f.delete()) { + throw new IOException("Failed to remove directory " + + translateNFSPath(path)); + } + } else { + throw new IOException(translateNFSPath(path) + + " is not an empty directory"); + } + } else { + throw new FileNotFoundException(translateNFSPath(path) + + " does not exist"); + } + } else { + throw new IOException(translateNFSPath(path) + + " is not a directory"); + } + } + + /** + * + * + * @param path + * @param attrs + * + * @throws PermissionDeniedException + * @throws IOException + * @throws FileNotFoundException + */ + public void setFileAttributes(String path, FileAttributes attrs) + throws PermissionDeniedException, IOException, FileNotFoundException { + // Since we cannot really set permissions, this should be ignored as we + // do not want applications to fail. + + /*String realPath = VirtualFileSystem.translateVFSPath(path); + throw new PermissionDeniedException( + "Cannot set file attributes using virtual file system for file " + + realPath);*/ + } + + /** + * + * + * @param handle + * @param attrs + * + * @throws PermissionDeniedException + * @throws IOException + * @throws InvalidHandleException + */ + public void setFileAttributes(byte[] handle, FileAttributes attrs) + throws PermissionDeniedException, IOException, InvalidHandleException { + } + + /** + * + * + * @param path + * + * @return + * + * @throws UnsupportedFileOperationException + * @throws FileNotFoundException + * @throws IOException + * @throws PermissionDeniedException + */ + public SftpFile readSymbolicLink(String path) + throws UnsupportedFileOperationException, FileNotFoundException, + IOException, PermissionDeniedException { + throw new UnsupportedFileOperationException( + "Symbolic links are not supported by the Virtual File System"); + } + + /** + * + * + * @param link + * @param target + * + * @throws UnsupportedFileOperationException + * @throws FileNotFoundException + * @throws IOException + * @throws PermissionDeniedException + */ + public void createSymbolicLink(String link, String target) + throws UnsupportedFileOperationException, FileNotFoundException, + IOException, PermissionDeniedException { + throw new UnsupportedFileOperationException( + "Symbolic links are not supported by the Virtual File System"); + } + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + */ + public boolean fileExists(String path) throws IOException { + File f = new File(VirtualFileSystem.translateVFSPath(path)); + + return f.exists(); + } + + public String getDefaultPath(String username) throws FileNotFoundException { + return getVFSHomeDirectory(username); + } + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + * @throws FileNotFoundException + */ + public String getCanonicalPath(String path) + throws IOException, FileNotFoundException { + File f = new File(VirtualFileSystem.translateVFSPath(path)); + + return f.getCanonicalPath(); + } + + /** + * + * + * @param path + * + * @return + * + * @throws FileNotFoundException + */ + public String getRealPath(String path) throws FileNotFoundException { + log.debug("Get real path for '" + path + "'"); + path = VirtualFileSystem.translateVFSPath(path); + log.debug("Translated VFS is '" + path + "'"); + path = VirtualFileSystem.translateNFSPath(path); + log.debug("Translated NFS is '" + path + "'"); + + return path; + } + + /** + * + * + * @param username + * @param path + * @param permissions + * + * @throws PermissionDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public void verifyPermissions(String username, String path, + String permissions) + throws PermissionDeniedException, FileNotFoundException, IOException { + String vfspath = translateNFSPath(path); + + if (permissionHandler == null) { + VFSMount mount = getMount(vfspath); + VFSPermission perm; + + if (mount.getPermissions().containsKey(SshThread.getCurrentThreadUser())) { + perm = (VFSPermission) mount.getPermissions().get(SshThread.getCurrentThreadUser()); + } else if (mount.getPermissions().containsKey("default")) { + perm = (VFSPermission) mount.getPermissions().get("default"); + } else { + throw new PermissionDeniedException( + "No permissions set for mount"); + } + + if (!perm.verifyPermissions(permissions)) { + throw new PermissionDeniedException("Permission denied for " + + translateNFSPath(path)); + } + } else { + permissionHandler.verifyPermissions(username, path, permissions); + } + } + + class OpenFile { + File f; + RandomAccessFile raf; + UnsignedInteger32 flags; + + public OpenFile(File f, RandomAccessFile raf, UnsignedInteger32 flags) { + this.f = f; + this.raf = raf; + this.flags = flags; + } + + public File getFile() { + return f; + } + + public RandomAccessFile getRandomAccessFile() { + return raf; + } + + public UnsignedInteger32 getFlags() { + return flags; + } + } + + class OpenDirectory { + File f; + File[] children; + int readpos = 0; + String path; + String realPath; + + public OpenDirectory(String realPath, String path, File f) { + this.path = path; + this.realPath = realPath; + this.f = f; + this.children = f.listFiles(); + } + + public File getFile() { + return f; + } + + public File[] getChildren() { + return children; + } + + public int getPosition() { + return readpos; + } + + public void setPosition(int readpos) { + this.readpos = readpos; + } + } +} diff --git a/src/com/sshtools/j2ssh/DirectoryOperation.java b/src/com/sshtools/j2ssh/DirectoryOperation.java new file mode 100644 index 0000000000000000000000000000000000000000..c20d3a818b76710f2a36183a4b4bb6e7f9d6d288 --- /dev/null +++ b/src/com/sshtools/j2ssh/DirectoryOperation.java @@ -0,0 +1,170 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import com.sshtools.j2ssh.sftp.SftpFile; + +import java.io.*; + +import java.util.*; + + +public class DirectoryOperation { + Vector unchangedFiles = new Vector(); + Vector newFiles = new Vector(); + Vector updatedFiles = new Vector(); + Vector deletedFiles = new Vector(); + Vector recursedDirectories = new Vector(); + + public DirectoryOperation() { + } + + protected void addNewFile(File f) { + newFiles.add(f); + } + + protected void addUpdatedFile(File f) { + updatedFiles.add(f); + } + + protected void addDeletedFile(File f) { + deletedFiles.add(f); + } + + protected void addUnchangedFile(File f) { + unchangedFiles.add(f); + } + + protected void addNewFile(SftpFile f) { + newFiles.add(f); + } + + protected void addUpdatedFile(SftpFile f) { + updatedFiles.add(f); + } + + protected void addDeletedFile(SftpFile f) { + deletedFiles.add(f); + } + + protected void addUnchangedFile(SftpFile f) { + unchangedFiles.add(f); + } + + public List getNewFiles() { + return newFiles; + } + + public List getUpdatedFiles() { + return updatedFiles; + } + + public List getUnchangedFiles() { + return unchangedFiles; + } + + public List getDeletedFiles() { + return deletedFiles; + } + + public boolean containsFile(File f) { + return unchangedFiles.contains(f) || newFiles.contains(f) || + updatedFiles.contains(f) || deletedFiles.contains(f) || + recursedDirectories.contains(f); + } + + public boolean containsFile(SftpFile f) { + return unchangedFiles.contains(f) || newFiles.contains(f) || + updatedFiles.contains(f) || deletedFiles.contains(f) || + recursedDirectories.contains(f.getAbsolutePath()); + } + + public void addDirectoryOperation(DirectoryOperation op, File f) { + updatedFiles.addAll(op.getUpdatedFiles()); + newFiles.addAll(op.getNewFiles()); + unchangedFiles.addAll(op.getUnchangedFiles()); + deletedFiles.addAll(op.getDeletedFiles()); + recursedDirectories.add(f); + } + + public int getFileCount() { + return newFiles.size() + updatedFiles.size(); + } + + public void addDirectoryOperation(DirectoryOperation op, String file) { + updatedFiles.addAll(op.getUpdatedFiles()); + newFiles.addAll(op.getNewFiles()); + unchangedFiles.addAll(op.getUnchangedFiles()); + deletedFiles.addAll(op.getDeletedFiles()); + recursedDirectories.add(file); + } + + public long getTransferSize() { + Object obj; + long size = 0; + SftpFile sftpfile; + File file; + + for (Iterator i = newFiles.iterator(); i.hasNext();) { + obj = i.next(); + + if (obj instanceof File) { + file = (File) obj; + + if (file.isFile()) { + size += file.length(); + } + } else if (obj instanceof SftpFile) { + sftpfile = (SftpFile) obj; + + if (sftpfile.isFile()) { + size += sftpfile.getAttributes().getSize().longValue(); + } + } + } + + for (Iterator i = updatedFiles.iterator(); i.hasNext();) { + obj = i.next(); + + if (obj instanceof File) { + file = (File) obj; + + if (file.isFile()) { + size += file.length(); + } + } else if (obj instanceof SftpFile) { + sftpfile = (SftpFile) obj; + + if (sftpfile.isFile()) { + size += sftpfile.getAttributes().getSize().longValue(); + } + } + } + + // Add a value for deleted files?? + return size; + } +} diff --git a/src/com/sshtools/j2ssh/FileTransferProgress.java b/src/com/sshtools/j2ssh/FileTransferProgress.java new file mode 100644 index 0000000000000000000000000000000000000000..7e87c89435b8ba61db0348d9a279795c04fb91df --- /dev/null +++ b/src/com/sshtools/j2ssh/FileTransferProgress.java @@ -0,0 +1,76 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + + +/** + * <p> + * Title: + * </p> + * + * <p> + * Description: + * </p> + * + * <p> + * Copyright: Copyright (c) 2003 + * </p> + * + * <p> + * Company: + * </p> + * + * @author Lee David Painter + * @version $Id: FileTransferProgress.java,v 1.10 2003/09/11 15:35:00 martianx Exp $ + */ +public interface FileTransferProgress { + /** + * + * + * @param bytesTotal + * @param remoteFile + */ + public void started(long bytesTotal, String remoteFile); + + /** + * + * + * @return + */ + public boolean isCancelled(); + + /** + * + * + * @param bytesSoFar + */ + public void progressed(long bytesSoFar); + + /** + * + */ + public void completed(); +} diff --git a/src/com/sshtools/j2ssh/ScpClient.java b/src/com/sshtools/j2ssh/ScpClient.java new file mode 100644 index 0000000000000000000000000000000000000000..482947b5f40265487070ce3cb5376f3edb474f71 --- /dev/null +++ b/src/com/sshtools/j2ssh/ScpClient.java @@ -0,0 +1,668 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import java.io.BufferedInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.connection.ChannelEventListener; +import com.sshtools.j2ssh.session.SessionChannelClient; + +/** + * <p> + * Implements a Secure Copy (SCP) client. This may be useful when the server + * does not support SFTP. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.18 $ + * + * @since 0.2.0 + */ +public final class ScpClient { + private SshClient ssh; + private File cwd; + private boolean verbose; + private ChannelEventListener eventListener; + /** + * <p> + * Creates an SCP client. CWD (Current working directory) will be the CWD + * of the process that started this JVM. + * </p> + * + * @param ssh A connected SshClient + * @param verbose Output verbose detail + * @param eventListener + * + * @since 0.2.0 + */ + public ScpClient(SshClient ssh, boolean verbose, + ChannelEventListener eventListener) { + this(new File(ConfigurationLoader.checkAndGetProperty("user.dir", ".")), + ssh, verbose, eventListener); + } + + /** + * <p> + * Creates an SCP client. + * </p> + * + * @param cwd The current local directory + * @param ssh A connected SshClient + * @param verbose Output verbose detail + * @param eventListener + * + * @since 0.2.0 + */ + public ScpClient(File cwd, SshClient ssh, boolean verbose, + ChannelEventListener eventListener) { + this.ssh = ssh; + this.cwd = cwd; + this.verbose = verbose; + this.eventListener = eventListener; + } + + /** + * <p> + * Uploads a <code>java.io.InputStream</code> to a remove server as file. + * You <strong>must</strong> supply the correct number of bytes that will + * be written. + * </p> + * + * @param in stream providing file + * @param length number of bytes that will be written + * @param localFile local file name + * @param remoteFile remote file name + * + * @throws IOException on any error + */ + public void put(InputStream in, long length, String localFile, + String remoteFile) throws IOException { + ScpChannel scp = new ScpChannel("scp -t " + (verbose ? "-v " : "") + + remoteFile); + scp.addEventListener(eventListener); + if (!ssh.openChannel(scp)) { + throw new IOException("Failed to open SCP channel"); + } + scp.waitForResponse(); + scp.writeStreamToRemote(in, length, localFile); + scp.close(); + } + + /** + * <p> + * Gets a remote file as an <code>java.io.InputStream</code>. + * </p> + * + * @param remoteFile remote file name + * + * @return stream + * + * @throws IOException on any error + */ + public InputStream get(String remoteFile) throws IOException { + ScpChannel scp = new ScpChannel("scp " + "-f " + (verbose ? "-v " : "") + + remoteFile); + scp.addEventListener(eventListener); + if (!ssh.openChannel(scp)) { + throw new IOException("Failed to open SCP Channel"); + } + return scp.readStreamFromRemote(); + } + + /** + * <p> + * Uploads a local file onto the remote server. + * </p> + * + * @param localFile The path to the local file relative to the local + * current directory; may be a file or directory + * @param remoteFile The path on the remote server, may be a file or + * directory + * @param recursive Copy the contents of a directory recursivly + * + * @throws IOException if an IO error occurs during the operation + * + * @since 0.2.0 + */ + public void put(String localFile, String remoteFile, boolean recursive) throws + IOException { + File lf = new File(localFile); + if (!lf.isAbsolute()) { + lf = new File(cwd, localFile); + } + if (!lf.exists()) { + throw new IOException(localFile + " does not exist"); + } + if (!lf.isFile() && !lf.isDirectory()) { + throw new IOException(localFile + + " is not a regular file or directory"); + } + if (lf.isDirectory() && !recursive) { + throw new IOException(localFile + + " is a directory, use recursive mode"); + } + if ( (remoteFile == null) || remoteFile.equals("")) { + remoteFile = "."; + } + ScpChannel scp = new ScpChannel("scp " + + (lf.isDirectory() ? "-d " : "") + "-t " + + (recursive ? "-r " : "") + + (verbose ? "-v " : "") + + remoteFile); + scp.addEventListener(eventListener); + if (!ssh.openChannel(scp)) { + throw new IOException("Failed to open SCP channel"); + } + scp.waitForResponse(); + scp.writeFileToRemote(lf, recursive); + scp.close(); + } + + /** + * <p> + * Uploads an array of local files onto the remote server. + * </p> + * + * @param localFiles an array of local files; may be files or directories + * @param remoteFile the path on the remote server, may be a file or + * directory1 + * @param recursive Copy the contents of directorys recursivly + * + * @throws IOException if an IO error occurs during the operation + * + * @since 0.2.0 + */ + public void put(String[] localFiles, String remoteFile, boolean recursive) throws + IOException { + if ( (remoteFile == null) || remoteFile.equals("")) { + remoteFile = "."; + } + if (localFiles.length == 1) { + put(localFiles[0], remoteFile, recursive); + } + else { + ScpChannel scp = new ScpChannel("scp " + "-d -t " + + (recursive ? "-r " : "") + + (verbose ? "-v " : "") + + remoteFile); + scp.addEventListener(eventListener); + if (!ssh.openChannel(scp)) { + throw new IOException("Failed to open SCP channel"); + } + scp.waitForResponse(); + for (int i = 0; i < localFiles.length; i++) { + File lf = new File(localFiles[i]); + if (!lf.isAbsolute()) { + lf = new File(cwd, localFiles[i]); + } + if (!lf.isFile() && !lf.isDirectory()) { + throw new IOException(lf.getName() + + " is not a regular file or directory"); + } + scp.writeFileToRemote(lf, recursive); + } + scp.close(); + } + } + + /** + * <p> + * Downloads an array of remote files to the local computer. + * </p> + * + * @param localFile The local path to place the files + * @param remoteFiles The path of the remote files + * @param recursive recursivly copy the contents of a directory + * + * @throws IOException if an IO error occurs during the operation + * + * @since 0.2.0 + */ + public void get(String localFile, String[] remoteFiles, boolean recursive) throws + IOException { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < remoteFiles.length; i++) { + buf.append("\""); + buf.append(remoteFiles[i]); + buf.append("\" "); + } + String remoteFile = buf.toString(); + remoteFile = remoteFile.trim(); + get(localFile, remoteFile, recursive); + } + + /** + * <p> + * Downloads a remote file onto the local computer. + * </p> + * + * @param localFile The path to place the file + * @param remoteFile The path of the file on the remote server + * @param recursive recursivly copy the contents of a directory + * + * @throws IOException if an IO error occurs during the operation + * + * @since 0.2.0 + */ + public void get(String localFile, String remoteFile, boolean recursive) throws + IOException { + if ( (localFile == null) || localFile.equals("")) { + localFile = "."; + } + File lf = new File(localFile); + if (!lf.isAbsolute()) { + lf = new File(cwd, localFile); + } + if (lf.exists() && !lf.isFile() && !lf.isDirectory()) { + throw new IOException(localFile + + " is not a regular file or directory"); + } + ScpChannel scp = new ScpChannel("scp " + "-f " + + (recursive ? "-r " : "") + + (verbose ? "-v " : "") + + remoteFile); + scp.addEventListener(eventListener); + if (!ssh.openChannel(scp)) { + throw new IOException("Failed to open SCP Channel"); + } + scp.readFromRemote(lf); + scp.close(); + } + + /** + * <p> + * Implements an SCP channel by extending the + * <code>SessionChannelClient</code>. + * </p> + * + * @since 0.2.0 + */ + class ScpChannel + extends SessionChannelClient { + byte[] buffer = new byte[16384]; + String cmd; + /** + * <p> + * Contruct the channel with the specified scp command. + * </p> + * + * @param cmd The scp command + * + * @since 0.2.0 + */ + ScpChannel(String cmd) { + this.cmd = cmd; + setName("scp"); + } + + /** + * <p> + * This implementation executes the scp command when the channel is + * opened. + * </p> + * + * @throws IOException + * + * @since 0.2.0 + */ + protected void onChannelOpen() throws IOException { + if (!executeCommand(cmd)) { + throw new IOException("Failed to execute the command " + cmd); + } + } + + /** + * <p> + * Writes a directory to the remote server. + * </p> + * + * @param dir The source directory + * @param recursive Add the contents of the directory recursivley + * + * @return true if the file was written, otherwise false + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + private boolean writeDirToRemote(File dir, boolean recursive) throws + IOException { + if (!recursive) { + writeError("File " + dir.getName() + + " is a directory, use recursive mode"); + return false; + } + String cmd = "D0755 0 " + dir.getName() + "\n"; + out.write(cmd.getBytes()); + waitForResponse(); + String[] list = dir.list(); + for (int i = 0; i < list.length; i++) { + File f = new File(dir, list[i]); + writeFileToRemote(f, recursive); + } + out.write("E\n".getBytes()); + return true; + } + + /** + * <p> + * Write a stream as a file to the remote server. You + * <strong>must</strong> supply the correct number of bytes that will + * be written. + * </p> + * + * @param in stream + * @param length number of bytes to write + * @param localName local file name + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + private void writeStreamToRemote(InputStream in, long length, + String localName) throws IOException { + String cmd = "C0644 " + length + " " + localName + "\n"; + out.write(cmd.getBytes()); + waitForResponse(); + writeCompleteFile(in, length); + writeOk(); + waitForResponse(); + } + + /** + * <p> + * Write a file to the remote server. + * </p> + * + * @param file The source file + * @param recursive Add the contents of the directory recursivley + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + private void writeFileToRemote(File file, boolean recursive) throws + IOException { + if (file.isDirectory()) { + if (!writeDirToRemote(file, recursive)) { + return; + } + } + else if (file.isFile()) { + String cmd = "C0644 " + file.length() + " " + file.getName() + + "\n"; + out.write(cmd.getBytes()); + waitForResponse(); + FileInputStream fi = new FileInputStream(file); + writeCompleteFile(fi, file.length()); + writeOk(); + } + else { + throw new IOException(file.getName() + " not valid for SCP"); + } + waitForResponse(); + } + + private void readFromRemote(File file) throws IOException { + String cmd; + String[] cmdParts = new String[3]; + writeOk(); + while (true) { + try { + cmd = readString(); + } + catch (EOFException e) { + return; + } + char cmdChar = cmd.charAt(0); + switch (cmdChar) { + case 'E': + writeOk(); + return; + case 'T': + throw new IOException("SCP time not supported: " + cmd); + case 'C': + case 'D': + String targetName = file.getAbsolutePath(); + parseCommand(cmd, cmdParts); + if (file.isDirectory()) { + targetName += (File.separator + cmdParts[2]); + } + File targetFile = new File(targetName); + if (cmdChar == 'D') { + if (targetFile.exists()) { + if (!targetFile.isDirectory()) { + String msg = "Invalid target " + + targetFile.getName() + + ", must be a directory"; + writeError(msg); + throw new IOException(msg); + } + } + else { + if (!targetFile.mkdir()) { + String msg = "Could not create directory: " + + targetFile.getName(); + writeError(msg); + throw new IOException(msg); + } + } + readFromRemote(targetFile); + continue; + } + FileOutputStream fo = new FileOutputStream(targetFile); + writeOk(); + long len = Long.parseLong(cmdParts[1]); + readCompleteFile(fo, len); + waitForResponse(); + writeOk(); + break; + default: + writeError("Unexpected cmd: " + cmd); + throw new IOException("SCP unexpected cmd: " + cmd); + } + } + } + + private InputStream readStreamFromRemote() throws IOException { + String cmd; + String[] cmdParts = new String[3]; + writeOk(); + try { + cmd = readString(); + } + catch (EOFException e) { + return null; + } + char cmdChar = cmd.charAt(0); + switch (cmdChar) { + case 'E': + writeOk(); + return null; + case 'T': + throw new IOException("SCP time not supported: " + cmd); + case 'D': + throw new IOException( + "Directories cannot be copied to a stream"); + case 'C': + parseCommand(cmd, cmdParts); + writeOk(); + long len = Long.parseLong(cmdParts[1]); + return new BufferedInputStream(new ScpInputStream(len, in, this), + 16 * 1024); + default: + writeError("Unexpected cmd: " + cmd); + throw new IOException("SCP unexpected cmd: " + cmd); + } + } + + private void parseCommand(String cmd, String[] cmdParts) throws IOException { + int l; + int r; + l = cmd.indexOf(' '); + r = cmd.indexOf(' ', l + 1); + if ( (l == -1) || (r == -1)) { + writeError("Syntax error in cmd"); + throw new IOException("Syntax error in cmd"); + } + cmdParts[0] = cmd.substring(1, l); + cmdParts[1] = cmd.substring(l + 1, r); + cmdParts[2] = cmd.substring(r + 1); + } + + private String readString() throws IOException { + int ch; + int i = 0; + while ( ( (ch = in.read()) != ( (int) '\n')) && (ch >= 0)) { + buffer[i++] = (byte) ch; + } + if (ch == -1) { + throw new EOFException("SCP returned unexpected EOF"); + } + if (buffer[0] == (byte) '\n') { + throw new IOException("Unexpected <NL>"); + } + if ( (buffer[0] == (byte) '\02') || (buffer[0] == (byte) '\01')) { + String msg = new String(buffer, 1, i - 1); + if (buffer[0] == (byte) '\02') { + throw new IOException(msg); + } + throw new IOException("SCP returned an unexpected error: " + + msg); + } + return new String(buffer, 0, i); + } + + private void waitForResponse() throws IOException { + int r = in.read(); + if (r == 0) { + // All is well, no error + return; + } + if (r == -1) { + throw new EOFException("SCP returned unexpected EOF"); + } + String msg = readString(); + if (r == (byte) '\02') { + throw new IOException(msg); + } + throw new IOException("SCP returned an unexpected error: " + msg); + } + + private void writeOk() throws IOException { + out.write(0); + } + + private void writeError(String reason) throws IOException { + out.write(1); + out.write(reason.getBytes()); + } + + private void writeCompleteFile(InputStream file, long size) throws + IOException { + int count = 0; + int read; + try { + while (count < size) { + read = file.read(buffer, 0, + (int) ( ( (size - count) < buffer.length) + ? (size - count) : buffer.length)); + if (read == -1) { + throw new EOFException("SCP received an unexpected EOF"); + } + count += read; + out.write(buffer, 0, read); + } + } + finally { + file.close(); + } + } + + private void readCompleteFile(FileOutputStream file, long size) throws + IOException { + int count = 0; + int read; + try { + while (count < size) { + read = in.read(buffer, 0, + (int) ( ( (size - count) < buffer.length) + ? (size - count) : buffer.length)); + if (read == -1) { + throw new EOFException("SCP received an unexpected EOF"); + } + count += read; + file.write(buffer, 0, read); + } + } + finally { + file.close(); + } + } + } + + class ScpInputStream + extends InputStream { + long length; + InputStream in; + long count; + ScpChannel channel; + ScpInputStream(long length, InputStream in, ScpChannel channel) { + this.length = length; + this.in = in; + this.channel = channel; + } + + public int read() throws IOException { + if (count == length) { + return -1; + } + if (count >= length) { + throw new EOFException("End of file."); + } + int r = in.read(); + if (r == -1) { + throw new EOFException("Unexpected EOF."); + } + count++; + if (count == length) { + channel.waitForResponse(); + channel.writeOk(); + } + return r; + } + + public void close() throws IOException { + channel.close(); + } + } +} diff --git a/src/com/sshtools/j2ssh/SftpClient.java b/src/com/sshtools/j2ssh/SftpClient.java new file mode 100644 index 0000000000000000000000000000000000000000..7791629f67202d267fa4d63ac51b95d9403091c9 --- /dev/null +++ b/src/com/sshtools/j2ssh/SftpClient.java @@ -0,0 +1,1240 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import com.sshtools.j2ssh.connection.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.sftp.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; +import java.util.Vector; + + +/** + * <p> + * Implements a Secure File Transfer (SFTP) client. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.44 $ + * + * @since 0.2.0 + */ +public class SftpClient { + SftpSubsystemClient sftp; + String cwd; + String lcwd; + private int BLOCKSIZE = 65535; + + // Default permissions is determined by default_permissions ^ umask + int umask = 0022; + int default_permissions = 0777; + + /** + * <p> + * Constructs the SFTP client. + * </p> + * + * @param ssh the <code>SshClient</code> instance + * + * @throws IOException if an IO error occurs + */ + SftpClient(SshClient ssh) throws IOException { + this(ssh, null); + } + + /** + * <p> + * Constructs the SFTP client with a given channel event listener. + * </p> + * + * @param ssh the <code>SshClient</code> instance + * @param eventListener an event listener implementation + * + * @throws IOException if an IO error occurs + */ + SftpClient(SshClient ssh, ChannelEventListener eventListener) + throws IOException { + if (!ssh.isConnected()) { + throw new IOException("SshClient is not connected"); + } + + this.sftp = ssh.openSftpChannel(eventListener); + + // Get the users default directory + cwd = sftp.getDefaultDirectory(); + lcwd = System.getProperty("user.home"); + } + + /** + * Sets the umask used by this client. + * @param umask + * @return the previous umask value + */ + public int umask(int umask) { + int old = umask; + this.umask = umask; + + return old; + } + + /** + * <p> + * Changes the working directory on the remote server. + * </p> + * + * @param dir the new working directory + * + * @throws IOException if an IO error occurs or the file does not exist + * @throws FileNotFoundException + * + * @since 0.2.0 + */ + public void cd(String dir) throws IOException { + try { + String actual; + + if (dir.equals("")) { + actual = sftp.getDefaultDirectory(); + } else { + actual = resolveRemotePath(dir); + actual = sftp.getAbsolutePath(actual); + } + + FileAttributes attr = sftp.getAttributes(actual); + + if (!attr.isDirectory()) { + throw new IOException(dir + " is not a directory"); + } + + cwd = actual; + } catch (IOException ex) { + throw new FileNotFoundException(dir + " could not be found"); + } + } + + private File resolveLocalPath(String path) throws IOException { + File f = new File(path); + + if (!f.isAbsolute()) { + f = new File(lcwd, path); + } + + return f; + } + + private String resolveRemotePath(String path) throws IOException { + verifyConnection(); + + String actual; + + if (!path.startsWith("/")) { + actual = cwd + (cwd.endsWith("/") ? "" : "/") + path; + } else { + actual = path; + } + + return actual; + } + + private void verifyConnection() throws SshException { + if (sftp.isClosed()) { + throw new SshException("The SFTP connection has been closed"); + } + } + + /** + * <p> + * Creates a new directory on the remote server. This method will throw an + * exception if the directory already exists. To create directories and + * disregard any errors use the <code>mkdirs</code> method. + * </p> + * + * @param dir the name of the new directory + * + * @throws IOException if an IO error occurs or if the directory already + * exists + * + * @since 0.2.0 + */ + public void mkdir(String dir) throws IOException { + String actual = resolveRemotePath(dir); + + try { + FileAttributes attrs = stat(actual); + + if (!attrs.isDirectory()) { + throw new IOException("File already exists named " + dir); + } + } catch (IOException ex) { + sftp.makeDirectory(actual); + chmod(default_permissions ^ umask, actual); + } + } + + /** + * <p> + * Create a directory or set of directories. This method will not fail even + * if the directories exist. It is advisable to test whether the directory + * exists before attempting an operation by using the <code>stat</code> + * method to return the directories attributes. + * </p> + * + * @param dir the path of directories to create. + */ + public void mkdirs(String dir) { + StringTokenizer tokens = new StringTokenizer(dir, "/"); + String path = dir.startsWith("/") ? "/" : ""; + + while (tokens.hasMoreElements()) { + path += (String) tokens.nextElement(); + + try { + stat(path); + } catch (IOException ex) { + try { + mkdir(path); + } catch (IOException ex2) { + } + } + + path += "/"; + } + } + + /** + * <p> + * Returns the absolute path name of the current remote working directory. + * </p> + * + * @return the absolute path of the remote working directory. + * + * @since 0.2.0 + */ + public String pwd() { + return cwd; + } + + /** + * <p> + * List the contents of the current remote working directory. + * </p> + * + * <p> + * Returns a list of <code>SftpFile</code> instances for the current + * working directory. + * </p> + * + * @return a list of SftpFile for the current working directory + * + * @throws IOException if an IO error occurs + * + * @see com.sshtools.j2ssh.sftp.SftpFile + * @since 0.2.0 + */ + public List ls() throws IOException { + return ls(cwd); + } + + /** + * <p> + * List the contents remote directory. + * </p> + * + * <p> + * Returns a list of <code>SftpFile</code> instances for the remote + * directory. + * </p> + * + * @param path the path on the remote server to list + * + * @return a list of SftpFile for the remote directory + * + * @throws IOException if an IO error occurs + * + * @see com.sshtools.j2ssh.sftp.SftpFile + * @since 0.2.0 + */ + public List ls(String path) throws IOException { + String actual = resolveRemotePath(path); + FileAttributes attrs = sftp.getAttributes(actual); + + if (!attrs.isDirectory()) { + throw new IOException(path + " is not a directory"); + } + + SftpFile file = sftp.openDirectory(actual); + Vector children = new Vector(); + + while (sftp.listChildren(file, children) > -1) { + ; + } + + file.close(); + + return children; + } + + /** + * <p> + * Changes the local working directory. + * </p> + * + * @param path the path to the new working directory + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + public void lcd(String path) throws IOException { + File actual; + + if (!isLocalAbsolutePath(path)) { + actual = new File(lcwd, path); + } else { + actual = new File(path); + } + + if (!actual.isDirectory()) { + throw new IOException(path + " is not a directory"); + } + + lcwd = actual.getCanonicalPath(); + } + + private static boolean isLocalAbsolutePath(String path) { + return (new File(path)).isAbsolute(); + } + + /** + * <p> + * Returns the absolute path to the local working directory. + * </p> + * + * @return the absolute path of the local working directory. + * + * @since 0.2.0 + */ + public String lpwd() { + return lcwd; + } + + /** + * <p> + * Download the remote file to the local computer. + * </p> + * + * @param path the path to the remote file + * @param progress + * + * @return + * + * @throws IOException if an IO error occurs of the file does not exist + * @throws TransferCancelledException + * + * @since 0.2.0 + */ + public FileAttributes get(String path, FileTransferProgress progress) + throws IOException, TransferCancelledException { + String localfile; + + if (path.lastIndexOf("/") > -1) { + localfile = path.substring(path.lastIndexOf("/") + 1); + } else { + localfile = path; + } + + return get(path, localfile, progress); + } + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + */ + public FileAttributes get(String path) throws IOException { + return get(path, (FileTransferProgress) null); + } + + private void transferFile(InputStream in, OutputStream out) + throws IOException, TransferCancelledException { + transferFile(in, out, null); + } + + private void transferFile( + InputStream in, + OutputStream out, + FileTransferProgress progress) + throws IOException, TransferCancelledException + { + try { + long bytesSoFar = 0; + byte[] buffer = new byte[BLOCKSIZE]; + int read; + + while ((read = in.read(buffer)) > -1) { + if ((progress != null) && progress.isCancelled()) { + throw new TransferCancelledException(); + } + + if (read > 0) { + out.write(buffer, 0, read); + + //out.flush(); + bytesSoFar += read; + + if (progress != null) { + progress.progressed(bytesSoFar); + } + } + } + } + finally { + try { + in.close(); + out.close(); + } + catch (IOException ex) { + } + } + } + + /** + * <p> + * Download the remote file to the local computer. If the paths provided + * are not absolute the current working directory is used. + * </p> + * + * @param remote the path/name of the remote file + * @param local the path/name to place the file on the local computer + * @param progress + * + * @return + * + * @throws IOException if an IO error occurs or the file does not exist + * @throws TransferCancelledException + * + * @since 0.2.0 + */ + public FileAttributes get(String remote, String local, + FileTransferProgress progress) + throws IOException, TransferCancelledException { + File localPath = resolveLocalPath(local); + + if (!localPath.exists()) { + localPath.getParentFile().mkdirs(); + localPath.createNewFile(); + } + + FileOutputStream out = new FileOutputStream(localPath); + + return get(remote, out, progress); + } + + /** + * + * + * @param remote + * @param local + * + * @return + * + * @throws IOException + */ + public FileAttributes get(String remote, String local) + throws IOException { + return get(remote, local, null); + } + + /** + * <p> + * Download the remote file writing it to the specified + * <code>OutputStream</code>. The OutputStream is closed by this mehtod + * even if the operation fails. + * </p> + * + * @param remote the path/name of the remote file + * @param local the OutputStream to write + * @param progress + * + * @return + * + * @throws IOException if an IO error occurs or the file does not exist + * @throws TransferCancelledException + * + * @since 0.2.0 + */ + public FileAttributes get(String remote, OutputStream local, + FileTransferProgress progress) + throws IOException, TransferCancelledException { + String remotePath = resolveRemotePath(remote); + FileAttributes attrs = stat(remotePath); + + if (progress != null) { + progress.started(attrs.getSize().longValue(), remotePath); + } + + SftpFileInputStream in = new SftpFileInputStream(sftp.openFile( + remotePath, SftpSubsystemClient.OPEN_READ)); + transferFile(in, local, progress); + + if (progress != null) { + progress.completed(); + } + + return attrs; + } + + /** + * + * + * @param remote + * @param local + * + * @return + * + * @throws IOException + */ + public FileAttributes get(String remote, OutputStream local) + throws IOException { + return get(remote, local, null); + } + + /** + * <p> + * Returns the state of the SFTP client. The client is closed if the + * underlying session channel is closed. Invoking the <code>quit</code> + * method of this object will close the underlying session channel. + * </p> + * + * @return true if the client is still connected, otherwise false + * + * @since 0.2.0 + */ + public boolean isClosed() { + return sftp.isClosed(); + } + + /** + * <p> + * Upload a file to the remote computer. + * </p> + * + * @param local the path/name of the local file + * @param progress + * + * @return + * + * @throws IOException if an IO error occurs or the file does not exist + * @throws TransferCancelledException + * + * @since 0.2.0 + */ + public void put(String local, FileTransferProgress progress) + throws IOException, TransferCancelledException { + File f = new File(local); + put(local, f.getName(), progress); + } + + /** + * + * + * @param local + * + * @return + * + * @throws IOException + */ + public void put(String local) throws IOException { + put(local, (FileTransferProgress) null); + } + + /** + * <p> + * Upload a file to the remote computer. If the paths provided are not + * absolute the current working directory is used. + * </p> + * + * @param local the path/name of the local file + * @param remote the path/name of the destination file + * @param progress + * + * @return + * + * @throws IOException if an IO error occurs or the file does not exist + * @throws TransferCancelledException + * + * @since 0.2.0 + */ + public void put(String local, String remote, FileTransferProgress progress) + throws IOException, TransferCancelledException { + File localPath = resolveLocalPath(local); + FileInputStream in = new FileInputStream(localPath); + + try { + FileAttributes attrs = stat(remote); + + if (attrs.isDirectory()) { + File f = new File(local); + remote += ((remote.endsWith("/") ? "" : "/") + f.getName()); + } + } catch (IOException ex) { + } + + put(in, remote, progress); + } + + /** + * + * + * @param local + * @param remote + * + * @return + * + * @throws IOException + */ + public void put(String local, String remote) throws IOException { + put(local, remote, null); + } + + /** + * <p> + * Upload a file to the remote computer reading from the specified <code> + * InputStream</code>. The InputStream is closed, even if the operation + * fails. + * </p> + * + * @param in the InputStream being read + * @param remote the path/name of the destination file + * @param progress + * + * @return + * + * @throws IOException if an IO error occurs + * @throws TransferCancelledException + * + * @since 0.2.0 + */ + public void put(InputStream in, String remote, FileTransferProgress progress) + throws IOException, TransferCancelledException { + String remotePath = resolveRemotePath(remote); + SftpFileOutputStream out; + FileAttributes attrs; + boolean newfile = false; + + try { + attrs = stat(remotePath); + out = new SftpFileOutputStream(sftp.openFile(remotePath, + SftpSubsystemClient.OPEN_CREATE | + SftpSubsystemClient.OPEN_TRUNCATE | + SftpSubsystemClient.OPEN_WRITE)); + } catch (IOException ex) { + attrs = new FileAttributes(); + newfile = true; + attrs.setPermissions(new UnsignedInteger32(default_permissions ^ + umask)); + out = new SftpFileOutputStream(sftp.openFile(remotePath, + SftpSubsystemClient.OPEN_CREATE | + SftpSubsystemClient.OPEN_WRITE, attrs)); + } + + if (progress != null) { + progress.started(in.available(), remotePath); + } + + transferFile(in, out, progress); + + if (progress != null) { + progress.completed(); + } + + // Set the permissions here since at creation they dont always work + if (newfile) { + chmod(default_permissions ^ umask, remotePath); + } + } + + /** + * + * + * @param in + * @param remote + * + * @return + * + * @throws IOException + */ + public void put(InputStream in, String remote) throws IOException { + put(in, remote, null); + } + + /** + * <p> + * Sets the user ID to owner for the file or directory. + * </p> + * + * @param uid numeric user id of the new owner + * @param path the path to the remote file/directory + * + * @throws IOException if an IO error occurs or the file does not exist + * + * @since 0.2.0 + */ + public void chown(int uid, String path) throws IOException { + String actual = resolveRemotePath(path); + FileAttributes attrs = sftp.getAttributes(actual); + attrs.setUID(new UnsignedInteger32(uid)); + sftp.setAttributes(actual, attrs); + } + + /** + * <p> + * Sets the group ID for the file or directory. + * </p> + * + * @param gid the numeric group id for the new group + * @param path the path to the remote file/directory + * + * @throws IOException if an IO error occurs or the file does not exist + * + * @since 0.2.0 + */ + public void chgrp(int gid, String path) throws IOException { + String actual = resolveRemotePath(path); + FileAttributes attrs = sftp.getAttributes(actual); + attrs.setGID(new UnsignedInteger32(gid)); + sftp.setAttributes(actual, attrs); + } + + /** + * <p> + * Changes the access permissions or modes of the specified file or + * directory. + * </p> + * + * <p> + * Modes determine who can read, change or execute a file. + * </p> + * <blockquote><pre>Absolute modes are octal numbers specifying the complete list of + * attributes for the files; you specify attributes by OR'ing together + * these bits. + * + * 0400 Individual read + * 0200 Individual write + * 0100 Individual execute (or list directory) + * 0040 Group read + * 0020 Group write + * 0010 Group execute + * 0004 Other read + * 0002 Other write + * 0001 Other execute </pre></blockquote> + * + * @param permissions the absolute mode of the file/directory + * @param path the path to the file/directory on the remote server + * + * @throws IOException if an IO error occurs or the file if not found + * + * @since 0.2.0 + */ + public void chmod(int permissions, String path) throws IOException { + String actual = resolveRemotePath(path); + sftp.changePermissions(actual, permissions); + } + + public void umask(String umask) throws IOException { + try { + this.umask = Integer.parseInt(umask, 8); + } catch (NumberFormatException ex) { + throw new IOException( + "umask must be 4 digit octal number e.g. 0022"); + } + } + + /** + * <p> + * Rename a file on the remote computer. + * </p> + * + * @param oldpath the old path + * @param newpath the new path + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + public void rename(String oldpath, String newpath) + throws IOException { + String from = resolveRemotePath(oldpath); + String to = resolveRemotePath(newpath); + sftp.renameFile(from, to); + } + + /** + * <p> + * Remove a file or directory from the remote computer. + * </p> + * + * @param path the path of the remote file/directory + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + public void rm(String path) throws IOException { + String actual = resolveRemotePath(path); + FileAttributes attrs = sftp.getAttributes(actual); + + if (attrs.isDirectory()) { + sftp.removeDirectory(actual); + } else { + sftp.removeFile(actual); + } + } + + /** + * + * + * @param path + * @param force + * @param recurse + * + * @throws IOException + */ + public void rm(String path, boolean force, boolean recurse) + throws IOException { + String actual = resolveRemotePath(path); + FileAttributes attrs = sftp.getAttributes(actual); + SftpFile file; + + if (attrs.isDirectory()) { + List list = ls(path); + + if (!force && (list.size() > 0)) { + throw new IOException( + "You cannot delete non-empty directory, use force=true to overide"); + } else { + for (Iterator it = list.iterator(); it.hasNext();) { + file = (SftpFile) it.next(); + + if (file.isDirectory() && !file.getFilename().equals(".") && + !file.getFilename().equals("..")) { + if (recurse) { + rm(file.getAbsolutePath(), force, recurse); + } else { + throw new IOException( + "Directory has contents, cannot delete without recurse=true"); + } + } else if (file.isFile()) { + sftp.removeFile(file.getAbsolutePath()); + } + } + } + + sftp.removeDirectory(actual); + } else { + sftp.removeFile(actual); + } + } + + /** + * <p> + * Create a symbolic link on the remote computer. + * </p> + * + * @param path the path to the existing file + * @param link the new link + * + * @throws IOException if an IO error occurs or the operation is not + * supported on the remote platform + * + * @since 0.2.0 + */ + public void symlink(String path, String link) throws IOException { + String actualPath = resolveRemotePath(path); + String actualLink = resolveRemotePath(link); + sftp.createSymbolicLink(actualPath, actualLink); + } + + /** + * <p> + * Returns the attributes of the file from the remote computer. + * </p> + * + * @param path the path of the file on the remote computer + * + * @return the attributes + * + * @throws IOException if an IO error occurs or the file does not exist + * + * @see com.sshtools.j2ssh.sftp.FileAttributes + * @since 0.2.0 + */ + public FileAttributes stat(String path) throws IOException { + String actual = resolveRemotePath(path); + + return sftp.getAttributes(actual); + } + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + */ + public String getAbsolutePath(String path) throws IOException { + String actual = resolveRemotePath(path); + + return sftp.getAbsolutePath(path); + } + + /** + * <p> + * Close the SFTP client. + * </p> + * + * @throws IOException + * + * @since 0.2.0 + */ + public void quit() throws IOException { + sftp.close(); + } + + /** + * + * + * @param localdir + * @param remotedir + * @param recurse + * @param sync + * @param commit + * @param progress + * + * @return + * + * @throws IOException + */ + public DirectoryOperation copyLocalDirectory(String localdir, + String remotedir, boolean recurse, boolean sync, boolean commit, + FileTransferProgress progress) throws IOException { + DirectoryOperation op = new DirectoryOperation(); + + // Record the previous + String pwd = pwd(); + String lpwd = lpwd(); + File local = resolveLocalPath(localdir); + remotedir = resolveRemotePath(remotedir); + remotedir += (remotedir.endsWith("/") ? "" : "/"); + remotedir += local.getName(); + remotedir += (remotedir.endsWith("/") ? "" : "/"); + + // Setup the remote directory if were committing + if (commit) { + try { + FileAttributes attrs = stat(remotedir); + } catch (IOException ex) { + mkdir(remotedir); + } + } + + // List the local files and verify against the remote server + File[] ls = local.listFiles(); + + if (ls != null) { + for (int i = 0; i < ls.length; i++) { + if (ls[i].isDirectory() && !ls[i].getName().equals(".") && + !ls[i].getName().equals("..")) { + if (recurse) { + File f = new File(local, ls[i].getName()); + op.addDirectoryOperation(copyLocalDirectory( + f.getAbsolutePath(), remotedir, recurse, sync, + commit, progress), f); + } + } else if (ls[i].isFile()) { + try { + FileAttributes attrs = stat(remotedir + + ls[i].getName()); + + if ((ls[i].length() == attrs.getSize().longValue()) && + ((ls[i].lastModified() / 1000) == attrs.getModifiedTime() + .longValue())) { + op.addUnchangedFile(ls[i]); + } else { + op.addUpdatedFile(ls[i]); + } + } catch (IOException ex1) { + op.addNewFile(ls[i]); + } + + if (commit) { + put(ls[i].getAbsolutePath(), + remotedir + ls[i].getName(), progress); + + FileAttributes attrs = stat(remotedir + + ls[i].getName()); + attrs.setTimes(new UnsignedInteger32( + ls[i].lastModified() / 1000), + new UnsignedInteger32(ls[i].lastModified() / 1000)); + sftp.setAttributes(remotedir + ls[i].getName(), attrs); + } + } + } + } + + if (sync) { + // List the contents of the new local directory and remove any + // files/directories that were not updated + try { + List files = ls(remotedir); + SftpFile file; + File f; + + for (Iterator it = files.iterator(); it.hasNext();) { + file = (SftpFile) it.next(); + + // Create a local file object to test for its existence + f = new File(local, file.getFilename()); + + if (!op.containsFile(file) && + !file.getFilename().equals(".") && + !file.getFilename().equals("..")) { + op.addDeletedFile(file); + + if (commit) { + if (file.isDirectory()) { + // Recurse through the directory, deleting stuff + recurseMarkForDeletion(file, op); + + if (commit) { + rm(file.getAbsolutePath(), true, true); + } + } else if (file.isFile()) { + rm(file.getAbsolutePath()); + } + } + } + } + } catch (IOException ex2) { + // Ignorew since if it does not exist we cant delete it + } + } + + // Return the operation details + return op; + } + + /** + * + * + * @param eventListener + */ + public void addEventListener(ChannelEventListener eventListener) { + sftp.addEventListener(eventListener); + } + + private void recurseMarkForDeletion(SftpFile file, DirectoryOperation op) + throws IOException { + List list = ls(file.getAbsolutePath()); + op.addDeletedFile(file); + + for (Iterator it = list.iterator(); it.hasNext();) { + file = (SftpFile) it.next(); + + if (file.isDirectory() && !file.getFilename().equals(".") && + !file.getFilename().equals("..")) { + recurseMarkForDeletion(file, op); + } else if (file.isFile()) { + op.addDeletedFile(file); + } + } + } + + private void recurseMarkForDeletion(File file, DirectoryOperation op) + throws IOException { + File[] list = file.listFiles(); + op.addDeletedFile(file); + + if (list != null) { + for (int i = 0; i < list.length; i++) { + file = list[i]; + + if (file.isDirectory() && !file.getName().equals(".") && + !file.getName().equals("..")) { + recurseMarkForDeletion(file, op); + } else if (file.isFile()) { + op.addDeletedFile(file); + } + } + } + } + + /** + * + * + * @param remotedir + * @param localdir + * @param recurse + * @param sync + * @param commit + * @param progress + * + * @return + * + * @throws IOException + */ + public DirectoryOperation copyRemoteDirectory(String remotedir, + String localdir, boolean recurse, boolean sync, boolean commit, + FileTransferProgress progress) throws IOException { + // Create an operation object to hold the information + DirectoryOperation op = new DirectoryOperation(); + + // Record the previous working directoies + String pwd = pwd(); + String lpwd = lpwd(); + cd(remotedir); + + // Setup the local cwd + String base = remotedir; + int idx = base.lastIndexOf('/'); + + if (idx != -1) { + base = base.substring(idx + 1); + } + + File local = new File(localdir, base); + + // File local = new File(localdir, remotedir); + if (!local.isAbsolute()) { + local = new File(lpwd(), localdir); + } + + if (!local.exists() && commit) { + local.mkdir(); + } + + List files = ls(); + SftpFile file; + File f; + + for (Iterator it = files.iterator(); it.hasNext();) { + file = (SftpFile) it.next(); + + if (file.isDirectory() && !file.getFilename().equals(".") && + !file.getFilename().equals("..")) { + if (recurse) { + f = new File(local, file.getFilename()); + op.addDirectoryOperation(copyRemoteDirectory( + file.getFilename(), local.getAbsolutePath(), + recurse, sync, commit, progress), f); + } + } else if (file.isFile()) { + f = new File(local, file.getFilename()); + + if (f.exists() && + (f.length() == file.getAttributes().getSize().longValue()) && + ((f.lastModified() / 1000) == file.getAttributes() + .getModifiedTime() + .longValue())) { + if (commit) { + op.addUnchangedFile(f); + } else { + op.addUnchangedFile(file); + } + + continue; + } + + if (f.exists()) { + if (commit) { + op.addUpdatedFile(f); + } else { + op.addUpdatedFile(file); + } + } else { + if (commit) { + op.addNewFile(f); + } else { + op.addNewFile(file); + } + } + + if (commit) { + FileAttributes attrs = get(file.getFilename(), + f.getAbsolutePath(), progress); + f.setLastModified(attrs.getModifiedTime().longValue() * 1000); + } + } + } + + if (sync) { + // List the contents of the new local directory and remove any + // files/directories that were not updated + File[] contents = local.listFiles(); + + if (contents != null) { + for (int i = 0; i < contents.length; i++) { + if (!op.containsFile(contents[i])) { + op.addDeletedFile(contents[i]); + + if (contents[i].isDirectory() && + !contents[i].getName().equals(".") && + !contents[i].getName().equals("..")) { + recurseMarkForDeletion(contents[i], op); + + if (commit) { + IOUtil.recurseDeleteDirectory(contents[i]); + } + } else if (commit) { + contents[i].delete(); + } + } + } + } + } + + cd(pwd); + + return op; + } +} diff --git a/src/com/sshtools/j2ssh/SshClient.java b/src/com/sshtools/j2ssh/SshClient.java new file mode 100644 index 0000000000000000000000000000000000000000..9cdde0bf10045b1de23f9aa3140ea2b46845e909 --- /dev/null +++ b/src/com/sshtools/j2ssh/SshClient.java @@ -0,0 +1,1406 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import com.sshtools.j2ssh.authentication.AuthenticationProtocolClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.configuration.SshConnectionProperties; +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelEventAdapter; +import com.sshtools.j2ssh.connection.ChannelEventListener; +import com.sshtools.j2ssh.connection.ChannelFactory; +import com.sshtools.j2ssh.connection.ConnectionProtocol; +import com.sshtools.j2ssh.forwarding.ForwardingClient; +import com.sshtools.j2ssh.net.TransportProvider; +import com.sshtools.j2ssh.net.TransportProviderFactory; +import com.sshtools.j2ssh.session.SessionChannelClient; +import com.sshtools.j2ssh.sftp.SftpSubsystemClient; +import com.sshtools.j2ssh.transport.ConsoleKnownHostsKeyVerification; +import com.sshtools.j2ssh.transport.HostKeyVerification; +import com.sshtools.j2ssh.transport.TransportProtocolClient; +import com.sshtools.j2ssh.transport.TransportProtocolState; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; +import com.sshtools.j2ssh.util.State; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.IOException; + +import java.net.UnknownHostException; + +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + + +/** + * <p> + * Implements an SSH client with methods to connect to a remote server and + * perform all necersary SSH functions such as SCP, SFTP, executing commands, + * starting the users shell and perform port forwarding. + * </p> + * + * <p> + * There are several steps to perform prior to performing the desired task. + * This involves the making the initial connection, authenticating the user + * and creating a session to execute a command, shell or subsystem and/or + * configuring the port forwarding manager. + * </p> + * + * <p> + * To create a connection use the following code:<br> + * <blockquote><pre> + * // Create a instance and connect SshClient + * ssh = new SshClient(); + * ssh.connect("hostname"); + * </pre></blockquote> + * Once this code has executed and returned + * the connection is ready for authentication:<br> + * <blockquote><pre> + * PasswordAuthenticationClient pwd = new PasswordAuthenticationClient(); + * pwd.setUsername("foo"); + * pwd.setPassword("xxxxx"); + * // Authenticate the user + * int result = ssh.authenticate(pwd); + * if(result==AuthenticationProtocolState.COMPLETED) { + * // Authentication complete + * } + * </pre></blockquote> + * Once authenticated the user's shell can be started:<br> + * <blockquote><pre> + * // Open a session channel + * SessionChannelClient session = + * ssh.openSessionChannel(); + * + * // Request a pseudo terminal, if you do not you may not see the prompt + * if(session.requestPseudoTerminal("ansi", 80, 24, 0, 0, "") { + * // Start the users shell + * if(session.startShell()) { + * // Do something with the session output + * session.getOutputStream().write("echo message\n"); + * .... + * } + * } + * </pre></blockquote> + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.75 $ + * + * @since 0.2.0 + */ +public class SshClient { + private static Log log = LogFactory.getLog(SshClient.class); + + /** + * The SSH Authentication protocol implementation for this SSH client. The + * SSH Authentication protocol runs over the SSH Transport protocol as a + * transport protocol service. + */ + protected AuthenticationProtocolClient authentication; + + /** + * The SSH Connection protocol implementation for this SSH client. The + * connection protocol runs over the SSH Transport protocol as a transport + * protocol service and is started by the authentication protocol after a + * successful authentication. + */ + protected ConnectionProtocol connection; + + /** Provides a high level management interface for SSH port forwarding. */ + protected ForwardingClient forwarding; + + /** The SSH Transport protocol implementation for this SSH Client. */ + protected TransportProtocolClient transport; + + /** The current state of the authentication for the current connection. */ + protected int authenticationState = AuthenticationProtocolState.READY; + + /** + * The timeout in milliseconds for the underlying transport provider + * (typically a Socket). + */ + protected int socketTimeout = 0; + + /** + * A Transport protocol event handler instance that receives notifications + * of transport layer events such as Socket timeouts and disconnection. + */ + protected SshEventAdapter eventHandler = null; + + /** The currently active channels for this SSH Client connection. */ + protected Vector activeChannels = new Vector(); + + /** + * An channel event listener implemention to maintain the active channel + * list. + */ + protected ActiveChannelEventListener activeChannelListener = new ActiveChannelEventListener(); + + /** + * Flag indicating whether the forwarding instance is created when the + * connection is made. + */ + protected boolean useDefaultForwarding = true; + + /** The currently active Sftp clients */ + private Vector activeSftpClients = new Vector(); + + /** + * <p> + * Contructs an unitilialized SshClient ready for connecting. + * </p> + */ + public SshClient() { + } + + /** + * <p> + * Returns the server's authentication banner. + * </p> + * + * <p> + * In some jurisdictions, sending a warning message before authentication + * may be relevant for getting legal protection. Many UNIX machines, for + * example, normally display text from `/etc/issue', or use "tcp wrappers" + * or similar software to display a banner before issuing a login prompt. + * </p> + * + * <p> + * The server may or may not send this message. Call this method to + * retrieve the message, specifying a timeout limit to wait for the + * message. + * </p> + * + * @param timeout The number of milliseconds to wait for the banner message + * before returning + * + * @return The server's banner message + * + * @exception IOException If an IO error occurs reading the message + * + * @since 0.2.0 + */ + public String getAuthenticationBanner(int timeout) + throws IOException { + if (authentication == null) { + return ""; + } else { + return authentication.getBannerMessage(timeout); + } + } + + /** + * <p> + * Returns the list of available authentication methods for a given user. + * </p> + * + * <p> + * A client may request a list of authentication methods that may continue + * by using the "none" authentication method.This method calls the "none" + * method and returns the available authentication methods. + * </p> + * + * @param username The name of the account for which you require the + * available authentication methods + * + * @return A list of Strings, for example "password", "publickey" & + * "keyboard-interactive" + * + * @exception IOException If an IO error occurs during the operation + * + * @since 0.2.0 + */ + public List getAvailableAuthMethods(String username) + throws IOException { + if (authentication != null) { + return authentication.getAvailableAuths(username, + connection.getServiceName()); + } else { + return null; + } + } + + /** + * <p> + * Returns the connection state of the client. + * </p> + * + * @return true if the client is connected, false otherwise + * + * @since 0.2.0 + */ + public boolean isConnected() { + State state = (transport == null) ? null : transport.getState(); + int value = (state == null) ? TransportProtocolState.DISCONNECTED + : state.getValue(); + + return ((value == TransportProtocolState.CONNECTED) || + (value == TransportProtocolState.PERFORMING_KEYEXCHANGE)); + } + + /** + * <p> + * Evaluate whether the client has successfully authenticated. + * </p> + * + * @return true if the client is authenticated, otherwise false + */ + public boolean isAuthenticated() { + return authenticationState == AuthenticationProtocolState.COMPLETE; + } + + /** + * <p> + * Returns the identification string sent by the server during protocol + * negotiation. For example "SSH-2.0-OpenSSH_p3.4". + * </p> + * + * @return The server's identification string. + * + * @since 0.2.0 + */ + public String getServerId() { + return transport.getRemoteId(); + } + + /** + * <p> + * Returns the server's public key supplied during key exchange. + * </p> + * + * @return the server's public key + * + * @since 0.2.0 + */ + public SshPublicKey getServerHostKey() { + return transport.getServerHostKey(); + } + + /** + * <p> + * Returns the transport protocol's connection state. + * </p> + * + * @return The transport protocol's state + * + * @since 0.2.0 + */ + public TransportProtocolState getConnectionState() { + return transport.getState(); + } + + /** + * <p> + * Returns the default port forwarding manager. + * </p> + * + * @return This connection's forwarding client + * + * @since 0.2.0 + */ + public ForwardingClient getForwardingClient() { + return forwarding; + } + + /** + * <p> + * Return's a rough guess at the server's EOL setting. This is simply + * derived from the identification string and should not be used as a cast + * iron proof on the EOL setting. + * </p> + * + * @return The transport protocol's EOL constant + * + * @since 0.2.0 + */ + public int getRemoteEOL() { + return transport.getRemoteEOL(); + } + + /** + * <p> + * Set the event handler for the underlying transport protocol. + * </p> + * <blockquote> + * <pre> + * ssh.setEventHandler(new TransportProtocolEventHandler() { + * + * public void onSocketTimeout(TransportProtocol transport) {<br> + * // Do something to handle the socket timeout<br> + * } + * + * public void onDisconnect(TransportProtocol transport) { + * // Perhaps some clean up? + * } + * }); + * </pre> + * </blockquote> + * + * @param eventHandler The event handler instance to receive transport + * protocol events + * + * @see com.sshtools.j2ssh.transport.TransportProtocolEventHandler + * @since 0.2.0 + */ + public void addEventHandler(SshEventAdapter eventHandler) { + // If were connected then add, otherwise store for later connection + if (transport != null) { + transport.addEventHandler(eventHandler); + authentication.addEventListener(eventHandler); + } else { + this.eventHandler = eventHandler; + } + } + + /** + * <p> + * Set's the socket timeout (in milliseconds) for the underlying transport + * provider. This MUST be called prior to connect. + * </p> + * <blockquote> + * SshClient ssh = new SshClient(); + * ssh.setSocketTimeout(30000); + * ssh.connect("hostname"); + * </blockquote> + * + * @param milliseconds The number of milliseconds without activity before + * the timeout event occurs + * + * @since 0.2.0 + */ + public void setSocketTimeout(int milliseconds) { + this.socketTimeout = milliseconds; + } + + /** + * <p> + * Return's a rough guess at the server's EOL setting. This is simply + * derived from the identification string and should not be used as a cast + * iron proof on the EOL setting. + * </p> + * + * @return The EOL string + * + * @since 0.2.0 + */ + public String getRemoteEOLString() { + return ((transport.getRemoteEOL() == TransportProtocolClient.EOL_CRLF) + ? "\r\n" : "\n"); + } + + /** + * Get the connection properties for this connection. + * + * @return + */ + public SshConnectionProperties getConnectionProperties() { + return transport.getProperties(); + } + + /** + * <p> + * Authenticate the user on the remote host. + * </p> + * + * <p> + * To authenticate the user, create an <code>SshAuthenticationClient</code> + * instance and configure it with the authentication details. + * </p> + * <code> PasswordAuthenticationClient pwd = new + * PasswordAuthenticationClient(); pwd.setUsername("root"); + * pwd.setPassword("xxxxxxxxx"); int result = ssh.authenticate(pwd); + * </code> + * + * <p> + * The method returns a result value will one of the public static values + * defined in <code>AuthenticationProtocolState</code>. These are<br> + * <br> + * COMPLETED - The authentication succeeded.<br> + * PARTIAL - The authentication succeeded but a further authentication + * method is required.<br> + * FAILED - The authentication failed.<br> + * CANCELLED - The user cancelled authentication (can only be returned + * when the user is prompted for information.<br> + * </p> + * + * @param auth A configured SshAuthenticationClient instance ready for + * authentication + * + * @return The authentication result + * + * @exception IOException If an IO error occurs during authentication + * + * @since 0.2.0 + */ + public int authenticate(SshAuthenticationClient auth) + throws IOException { + // Do the authentication + authenticationState = authentication.authenticate(auth, connection); + + if ((authenticationState == AuthenticationProtocolState.COMPLETE) && + useDefaultForwarding) { + // Use some method to synchronize forwardings on the ForwardingClient + forwarding.synchronizeConfiguration(transport.getProperties()); + } + + return authenticationState; + } + + /** + * <p> + * Determine whether a private/public key pair will be accepted for public + * key authentication. + * </p> + * + * <p> + * When using public key authentication, the signing of data could take + * some time depending upon the available machine resources. By calling + * this method, you can determine whether the server will accept a key for + * authentication by providing the public key. The server will verify the + * key against the user's authorized keys and return true should the + * public key be authorized. The caller can then proceed with the private + * key operation. + * </p> + * + * @param username The username for authentication + * @param key The public key for which authentication will be attempted + * + * @return true if the server will accept the key, otherwise false + * + * @exception IOException If an IO error occurs during the operation + * @throws SshException + * + * @since 0.2.0 + */ + public boolean acceptsKey(String username, SshPublicKey key) + throws IOException { + if (authenticationState != AuthenticationProtocolState.COMPLETE) { + PublicKeyAuthenticationClient pk = new PublicKeyAuthenticationClient(); + + return pk.acceptsKey(authentication, username, + connection.getServiceName(), key); + } else { + throw new SshException("Authentication has been completed!"); + } + } + + /** + * <p> + * Connect the client to the server using default connection properties. + * </p> + * + * <p> + * This call attempts to connect to the hostname specified on the standard + * SSH port of 22 and uses all the default connection properties. This + * call is the equivilent of calling: + * </p> + * <blockquote><pre> + * SshConnectionProperties properties = new + * SshConnectionProperties(); + * properties.setHostname("hostname"); + * ssh.connect(properties); + * </pre></blockquote> + * + * @param hostname The hostname of the server to connect + * + * @exception IOException If an IO error occurs during the connect + * operation + * + * @see #connect(com.sshtools.j2ssh.configuration.SshConnectionProperties) + * @since 0.2.0 + */ + public void connect(String hostname) throws IOException { + connect(hostname, 22, new ConsoleKnownHostsKeyVerification()); + } + + /** + * <p> + * Connect the client to the server using the default connection + * properties. + * </p> + * + * <p> + * This call attempts to connect to the hostname specified on the standard + * SSH port of 22 and uses all the default connection properties. When + * this method returns the connection has been established, the server's + * identity been verified and the connection is ready for user + * authentication. Host key verification will be performed using the host + * key verification instance provided: + * </p> + * <blockquote><pre> + * // Connect and consult $HOME/.ssh/known_hosts + * ssh.connect("hostname", new ConsoleKnownHostsKeyVerification()); + * // Connect and allow any host + * ssh.connect("hostname", new + * IgnoreHostKeyVerification()); + * </pre></blockquote> + * + * <p> + * You can provide your own host key verification process by implementing + * the <code>HostKeyVerification</code> interface. + * </p> + * + * @param hostname The hostname of the server to connect + * @param hosts The host key verification instance to consult for host key + * validation + * + * @exception IOException If an IO error occurs during the connect + * operation + * + * @see #connect(com.sshtools.j2ssh.configuration.SshConnectionProperties, + * com.sshtools.j2ssh.transport.HostKeyVerification) + * @since 0.2.0 + */ + public void connect(String hostname, HostKeyVerification hosts) + throws IOException { + connect(hostname, 22, hosts); + } + + /** + * <p> + * Connect the client to the server on a specified port with default + * connection properties. + * </p> + * + * <p> + * This call attempts to connect to the hostname and port specified. This + * call is the equivilent of calling: + * </p> + * <blockquote></pre> + * SshConnectionProperties properties = new + * SshConnectionProperties(); + * properties.setHostname("hostname"); + * properties.setPort(10022); + * ssh.connect(properties); + * </pre></blockquote> + * + * @param hostname The hostname of the server to connect + * @param port The port to connect + * + * @exception IOException If an IO error occurs during the connect + * operation + * + * @see #connect(com.sshtools.j2ssh.configuration.SshConnectionProperties) + * @since 0.2.0 + */ + public void connect(String hostname, int port) throws IOException { + connect(hostname, port, new ConsoleKnownHostsKeyVerification()); + } + + /** + * <p> + * Connect the client to the server on a specified port with default + * connection properties. + * </p> + * + * <p> + * This call attempts to connect to the hostname and port specified. When + * this method returns the connection has been established, the server's + * identity been verified and the connection is ready for user + * authentication. Host key verification will be performed using the host + * key verification instance provided: + * </p> + * <blockquote><pre> + * // Connect and consult $HOME/.ssh/known_hosts + * ssh.connect("hostname", new ConsoleKnownHostsKeyVerification()); + * // Connect and allow any host + * ssh.connect("hostname", new + * IgnoreHostKeyVerification()); + * </pre></blockquote> + * + * <p> + * You can provide your own host key verification process by implementing + * the <code>HostKeyVerification</code> interface. + * </p> + * + * @param hostname The hostname of the server to connect + * @param port The port to connect + * @param hosts The host key verification instance to consult for host key + * validation + * + * @exception IOException If an IO error occurs during the connect + * operation + * + * @see #connect(com.sshtools.j2ssh.configuration.SshConnectionProperties, + * com.sshtools.j2ssh.transport.HostKeyVerification) + * @since 0.2.0 + */ + public void connect(String hostname, int port, HostKeyVerification hosts) + throws IOException { + SshConnectionProperties properties = new SshConnectionProperties(); + properties.setHost(hostname); + properties.setPort(port); + connect(properties, hosts); + } + + /** + * <p> + * Connect the client to the server with the specified properties. + * </p> + * + * <p> + * This call attempts to connect to using the connection properties + * specified. When this method returns the connection has been + * established, the server's identity been verified and the connection is + * ready for user authentication. To use this method first create a + * properties instance and set the required fields. + * </p> + * <blockquote><pre> + * SshConnectionProperties properties = new + * SshConnectionProperties(); + * properties.setHostname("hostname"); + * properties.setPort(10022); + * properties.setPrefCSEncryption("blowfish-cbc"); + * ssh.connect(properties); + * </pre></blockquote> + * + * <p> + * Host key verification will be performed using + * <code>ConsoleKnownHostsKeyVerification</code> and so this call is the + * equivilent of calling: + * </p> + * <blockquote><pre> + * ssh.connect("hostname", new ConsoleKnownHostsKeyVerification()); + * </pre></blockquote> + * + * <p> + * If the key is not matched to any keys already in the + * $HOME/.ssh/known_hosts file, the user will be prompted via the console + * to confirm the identity of the remote server. The user will receive the + * following prompt. + * </p> + * <code> The host shell.sourceforge.net is currently unknown to the system + * The host key fingerprint is: 1024: 4c 68 3 d4 5c 58 a6 1d 9d 17 13 24 + * 14 48 ba 99 Do you want to allow this host key? [Yes|No|Always]: + * </code> + * + * <p> + * Selecting the "always" option will write the key to the known_hosts + * file. + * </p> + * + * @param properties The connection properties + * + * @exception IOException If an IO error occurs during the connect + * operation + * + * @since 0.2.0 + */ + public void connect(SshConnectionProperties properties) + throws IOException { + connect(properties, new ConsoleKnownHostsKeyVerification()); + } + + /** + * <p> + * Connect the client to the server with the specified properties. + * </p> + * + * <p> + * This call attempts to connect to using the connection properties + * specified. When this method returns the connection has been + * established, the server's identity been verified and the connection is + * ready for user authentication. To use this method first create a + * properties instance and set the required fields. + * </p> + * <blockquote><pre> + * SshConnectionProperties properties = new + * SshConnectionProperties(); + * properties.setHostname("hostname"); + * properties.setPort(22); // Defaults to 22 + * // Set the prefered client->server encryption + * ssh.setPrefCSEncryption("blowfish-cbc"); + * // Set the prefered server->client encrpytion + * ssh.setPrefSCEncrpyion("3des-cbc"); + * ssh.connect(properties); + * </pre></blockquote> + * + * <p> + * Host key verification will be performed using the host key verification + * instance provided:<br> + * <blockquote><pre> + * // Connect and consult $HOME/.ssh/known_hosts + * ssh.connect("hostname", new ConsoleKnownHostsKeyVerification()); + * // Connect and allow any host + * ssh.connect("hostname", new + * IgnoreHostKeyVerification()); + * </pre></blockquote> + * You can provide your own host key verification process by implementing the + * <code>HostKeyVerification</code> interface. + * </p> + * + * @param properties The connection properties + * @param hostVerification The host key verification instance to consult + * for host key validation + * + * @exception UnknownHostException If the host is unknown + * @exception IOException If an IO error occurs during the connect + * operation + * + * @since 0.2.0 + */ + public void connect(SshConnectionProperties properties, + HostKeyVerification hostVerification) + throws UnknownHostException, IOException { + TransportProvider provider = TransportProviderFactory.connectTransportProvider(properties /*, connectTimeout*/, + socketTimeout); + + // Start the transport protocol + transport = new TransportProtocolClient(hostVerification); + transport.addEventHandler(eventHandler); + transport.startTransportProtocol(provider, properties); + + // Start the authentication protocol + authentication = new AuthenticationProtocolClient(); + authentication.addEventListener(eventHandler); + transport.requestService(authentication); + connection = new ConnectionProtocol(); + + if (useDefaultForwarding) { + forwarding = new ForwardingClient(connection); + } + } + + /** + * <p> + * Sets the timeout value for the key exchange. + * </p> + * + * <p> + * When this time limit is reached the transport protocol will initiate a + * key re-exchange. The default value is one hour with the minumin timeout + * being 60 seconds. + * </p> + * + * @param seconds The number of seconds beofre key re-exchange + * + * @exception IOException If the timeout value is invalid + * + * @since 0.2.0 + */ + public void setKexTimeout(long seconds) throws IOException { + transport.setKexTimeout(seconds); + } + + /** + * <p> + * Sets the key exchance transfer limit in kilobytes. + * </p> + * + * <p> + * Once this amount of data has been transfered the transport protocol will + * initiate a key re-exchange. The default value is one gigabyte of data + * with the mimimun value of 10 kilobytes. + * </p> + * + * @param kilobytes The data transfer limit in kilobytes + * + * @exception IOException If the data transfer limit is invalid + */ + public void setKexTransferLimit(long kilobytes) throws IOException { + transport.setKexTransferLimit(kilobytes); + } + + /** + * <p> + * Set's the send ignore flag to send random data packets. + * </p> + * + * <p> + * If this flag is set to true, then the transport protocol will send + * additional SSH_MSG_IGNORE packets with random data. + * </p> + * + * @param sendIgnore true if you want to turn on random packet data, + * otherwise false + * + * @since 0.2.0 + */ + public void setSendIgnore(boolean sendIgnore) { + transport.setSendIgnore(sendIgnore); + } + + /** + * <p> + * Turn the default forwarding manager on/off. + * </p> + * + * <p> + * If this flag is set to false before connection, the client will not + * create a port forwarding manager. Use this to provide you own + * forwarding implementation. + * </p> + * + * @param useDefaultForwarding Set to false if you not wish to use the + * default forwarding manager. + * + * @since 0.2.0 + */ + public void setUseDefaultForwarding(boolean useDefaultForwarding) { + this.useDefaultForwarding = useDefaultForwarding; + } + + /** + * <p> + * Disconnect the client. + * </p> + * + * @since 0.2.0 + */ + public void disconnect() { + if (connection != null) { + connection.stop(); + } + + if (transport != null) { + transport.disconnect("Terminating connection"); + } + } + + /** + * <p> + * Returns the number of bytes transmitted to the remote server. + * </p> + * + * @return The number of bytes transmitted + * + * @since 0.2.0 + */ + public long getOutgoingByteCount() { + return transport.getOutgoingByteCount(); + } + + /** + * <p> + * Returns the number of bytes received from the remote server. + * </p> + * + * @return The number of bytes received + * + * @since 0.2.0 + */ + public long getIncomingByteCount() { + return transport.getIncomingByteCount(); + } + + /** + * <p> + * Returns the number of active channels for this client. + * </p> + * + * <p> + * This is the total count of sessions, port forwarding, sftp, scp and + * custom channels currently open. + * </p> + * + * @return The number of active channels + * + * @since 0.2.0 + */ + public int getActiveChannelCount() { + synchronized (activeChannels) { + return activeChannels.size(); + } + } + + /** + * <p> + * Returns the list of active channels. + * </p> + * + * @return The list of active channels + * + * @since 0.2.0 + */ + public List getActiveChannels() { + synchronized (activeChannels) { + return (List) activeChannels.clone(); + } + } + + /** + * <p> + * Returns true if there is an active session channel of the specified + * type. + * </p> + * + * <p> + * When a session is created, it is assigned a default type. For instance, + * when a session is created it as a type of "uninitialized"; however when + * a shell is started on the session, the type is set to "shell". This + * also occurs for commands where the type is set to the command which is + * executed and subsystems where the type is set to the subsystem name. + * This allows each session to be saved in the active session channel's + * list and recalled later. It is also possible to set the session + * channel's type using the setSessionType method of the + * <code>SessionChannelClient</code> class. + * </p> + * <blockquote><pre> + * if(ssh.hasActiveSession("shell")) { + * SessionChannelClient session = + * ssh.getActiveSession("shell"); + * } + * </pre></blockquote> + * + * @param type The string specifying the channel type + * + * @return true if an active session channel exists, otherwise false + * + * @since 0.2.0 + */ + public boolean hasActiveSession(String type) { + Iterator it = activeChannels.iterator(); + Object obj; + + while (it.hasNext()) { + obj = it.next(); + + if (obj instanceof SessionChannelClient) { + if (((SessionChannelClient) obj).getSessionType().equals(type)) { + return true; + } + } + } + + return false; + } + + /** + * <p> + * Returns the active session channel of the given type. + * </p> + * + * @param type The type fo session channel + * + * @return The session channel instance + * + * @exception IOException If the session type does not exist + * + * @since 0.2.0 + */ + public SessionChannelClient getActiveSession(String type) + throws IOException { + Iterator it = activeChannels.iterator(); + Object obj; + + while (it.hasNext()) { + obj = it.next(); + + if (obj instanceof SessionChannelClient) { + if (((SessionChannelClient) obj).getSessionType().equals(type)) { + return (SessionChannelClient) obj; + } + } + } + + throw new IOException("There are no active " + type + " sessions"); + } + + /** + * Determine whether the channel supplied is an active channel + * + * @param channel + * + * @return + */ + public boolean isActiveChannel(Channel channel) { + return activeChannels.contains(channel); + } + + /** + * <p> + * Open's a session channel on the remote server. + * </p> + * + * <p> + * A session channel may be used to start the user's shell, execute a + * command or start a subsystem such as SFTP. + * </p> + * + * @return An new session channel + * + * @exception IOException If authentication has not been completed, the + * server refuses to open the channel or a general IO error + * occurs + * + * @see com.sshtools.j2ssh.session.SessionChannelClient + * @since 0.2.0 + */ + public SessionChannelClient openSessionChannel() throws IOException { + return openSessionChannel(null); + } + + /** + * + * <p> + * Open's a session channel on the remote server. + * </p> + * + * <p> + * A session channel may be used to start the user's shell, execute a + * command or start a subsystem such as SFTP. + * </p> + * + * @param eventListener an event listner interface to add to the channel + * + * @return + * + * @throws IOException + * @throws SshException + */ + public SessionChannelClient openSessionChannel( + ChannelEventListener eventListener) throws IOException { + if (authenticationState != AuthenticationProtocolState.COMPLETE) { + throw new SshException("Authentication has not been completed!"); + } + + SessionChannelClient session = new SessionChannelClient(); + session.addEventListener(activeChannelListener); + + if (!connection.openChannel(session, eventListener)) { + throw new SshException("The server refused to open a session"); + } + + return session; + } + + /** + * <p> + * Open an SFTP client for file transfer operations. + * </p> + * <blockquote><pre> + * SftpClient sftp = ssh.openSftpClient(); + * sftp.cd("foo"); + * sftp.put("somefile.txt"); + * sftp.quit(); + * </pre></blockquote> + * + * @return Returns an initialized SFTP client + * + * @exception IOException If an IO error occurs during the operation + * + * @see SftpClient + * @since 0.2.0 + */ + public SftpClient openSftpClient() throws IOException { + return openSftpClient(null); + } + + /** + * <p> + * Open an SFTP client for file transfer operations. Adds the supplied + * event listener to the underlying channel. + * </p> + * + * @param eventListener + * + * @return + * + * @throws IOException + */ + public SftpClient openSftpClient(ChannelEventListener eventListener) + throws IOException { + SftpClient sftp = new SftpClient(this, eventListener); + activeSftpClients.add(sftp); + + return sftp; + } + + /** + * Determine if there are existing sftp clients open + * + * @return + */ + public boolean hasActiveSftpClient() { + synchronized (activeSftpClients) { + return activeSftpClients.size() > 0; + } + } + + /** + * Get an active sftp client + * + * @return + * + * @throws IOException + * @throws SshException + */ + public SftpClient getActiveSftpClient() throws IOException { + synchronized (activeSftpClients) { + if (activeSftpClients.size() > 0) { + return (SftpClient) activeSftpClients.get(0); + } else { + throw new SshException("There are no active SFTP clients"); + } + } + } + + /** + * <p> + * Open an SCP client for file transfer operations where SFTP is not + * supported. + * </p> + * + * <p> + * Sets the local working directory to the user's home directory + * </p> + * <blockquote><pre> + * ScpClient scp = ssh.openScpClient(); + * scp.put("somefile.txt"); + * </pre></blockquote> + * + * @return An initialized SCP client + * + * @exception IOException If an IO error occurs during the operation + * + * @see ScpClient + * @since 0.2.0 + */ + public ScpClient openScpClient() throws IOException { + return new ScpClient(new File(System.getProperty("user.home")), this, + false, activeChannelListener); + } + + /** + * <p> + * Open an SCP client for file transfer operations where SFTP is not + * supported. + * </p> + * + * <p> + * This method sets a local current working directory. + * </p> + * <blockquote><pre> + * ScpClient scp = ssh.openScpClient("foo"); + * scp.put("somefile.txt"); + * </pre></blockquote> + * + * @param cwd The local directory as the base for all local files + * + * @return An intialized SCP client + * + * @exception IOException If an IO error occurs during the operation + * + * @see SftpClient + * @since 0.2.0 + */ + public ScpClient openScpClient(File cwd) throws IOException { + return new ScpClient(cwd, this, false, activeChannelListener); + } + + /** + * <p> + * Open's an Sftp channel. + * </p> + * + * <p> + * Use this sftp channel if you require a lower level api into the SFTP + * protocol. + * </p> + * + * @return an initialized sftp subsystem instance + * + * @throws IOException if an IO error occurs or the channel cannot be + * opened + * + * @since 0.2.0 + */ + public SftpSubsystemClient openSftpChannel() throws IOException { + return openSftpChannel(null); + } + + /** + * Open an SftpSubsystemChannel. For advanced use only + * + * @param eventListener + * + * @return + * + * @throws IOException + * @throws SshException + */ + public SftpSubsystemClient openSftpChannel( + ChannelEventListener eventListener) throws IOException { + SessionChannelClient session = openSessionChannel(eventListener); + SftpSubsystemClient sftp = new SftpSubsystemClient(); + + if (!openChannel(sftp)) { + throw new SshException("The SFTP subsystem failed to start"); + } + + // Initialize SFTP + if (!sftp.initialize()) { + throw new SshException( + "The SFTP Subsystem could not be initialized"); + } + + return sftp; + } + + /** + * <p> + * Open's a channel. + * </p> + * + * <p> + * Call this method to open a custom channel. This method is used by all + * other channel opening methods. For example the openSessionChannel + * method could be implemented as:<br> + * <blockquote><pre> + * SessionChannelClient session = + * new SessionChannelClient(); + * if(ssh.openChannel(session)) { + * // Channel is now open + * } + * </pre></blockquote> + * </p> + * + * @param channel + * + * @return true if the channel was opened, otherwise false + * + * @exception IOException if an IO error occurs + * @throws SshException + * + * @since 0.2.0 + */ + public boolean openChannel(Channel channel) throws IOException { + if (authenticationState != AuthenticationProtocolState.COMPLETE) { + throw new SshException("Authentication has not been completed!"); + } + + // Open the channel providing our channel listener so we can track + return connection.openChannel(channel, activeChannelListener); + } + + /** + * <p> + * Instructs the underlying connection protocol to allow channels of the + * given type to be opened by the server. + * </p> + * + * <p> + * The client does not allow channels to be opened by default. Call this + * method to allow the server to open channels by providing a + * <code>ChannelFactory</code> implementation to create instances upon + * request. + * </p> + * + * @param channelName The channel type name + * @param cf The factory implementation that will create instances of the + * channel when a channel open request is recieved. + * + * @exception IOException if an IO error occurs + * + * @since 0.2.0 + */ + public void allowChannelOpen(String channelName, ChannelFactory cf) + throws IOException { + connection.addChannelFactory(channelName, cf); + } + + /** + * <p> + * Stops the specified channel type from being opended. + * </p> + * + * @param channelName The channel type name + * + * @throws IOException if an IO error occurs + * + * @since 0.2.1 + */ + public void denyChannelOpen(String channelName) throws IOException { + connection.removeChannelFactory(channelName); + } + + /** + * <p> + * Send a global request to the server. + * </p> + * + * <p> + * The SSH specification provides a global request mechanism which is used + * for starting/stopping remote forwarding. This is a general mechanism + * which can be used for other purposes if the server supports the global + * requests. + * </p> + * + * @param requestName The name of the global request + * @param wantReply true if the server should send an explict reply + * @param requestData the global request data + * + * @return true if the global request succeeded or wantReply==false, + * otherwise false + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + public byte[] sendGlobalRequest(String requestName, boolean wantReply, + byte[] requestData) throws IOException { + return connection.sendGlobalRequest(requestName, wantReply, requestData); + } + + /** + * <p> + * Implements the <code>ChannelEventListener</code> interface to provide + * real time tracking of active channels. + * </p> + */ + class ActiveChannelEventListener extends ChannelEventAdapter { + /** + * <p> + * Adds the channel to the active channel list. + * </p> + * + * @param channel The channel being opened + */ + public void onChannelOpen(Channel channel) { + synchronized (activeChannels) { + activeChannels.add(channel); + } + } + + /** + * <p> + * Removes the closed channel from the clients active channels list. + * </p> + * + * @param channel The channle being closed + */ + public void onChannelClose(Channel channel) { + synchronized (activeChannels) { + activeChannels.remove(channel); + } + } + } +} diff --git a/src/com/sshtools/j2ssh/SshEventAdapter.java b/src/com/sshtools/j2ssh/SshEventAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..4add19d7d81f2c2e38380bff88a073b4976f89ab --- /dev/null +++ b/src/com/sshtools/j2ssh/SshEventAdapter.java @@ -0,0 +1,76 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import com.sshtools.j2ssh.authentication.AuthenticationProtocolListener; +import com.sshtools.j2ssh.transport.TransportProtocol; +import com.sshtools.j2ssh.transport.TransportProtocolEventHandler; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.8 $ + */ +public class SshEventAdapter implements TransportProtocolEventHandler, + AuthenticationProtocolListener { + /** + * Creates a new SshEventAdapter object. + */ + public SshEventAdapter() { + } + + /** + * + * + * @param transport + */ + public void onSocketTimeout(TransportProtocol transport) { + } + + /** + * + * + * @param transport + */ + public void onDisconnect(TransportProtocol transport) { + } + + /** + * + * + * @param transport + */ + public void onConnected(TransportProtocol transport) { + } + + /** + * + */ + public void onAuthenticationComplete() { + } +} diff --git a/src/com/sshtools/j2ssh/SshException.java b/src/com/sshtools/j2ssh/SshException.java new file mode 100644 index 0000000000000000000000000000000000000000..90ab152672044ebb34845b34100431e9e7a7153d --- /dev/null +++ b/src/com/sshtools/j2ssh/SshException.java @@ -0,0 +1,68 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import java.io.*; + + +/** + * <p> + * The base exception for all exceptions thrown within the J2SSH application + * framework. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.20 $ + * + * @since 0.2.0 + */ +public class SshException extends IOException { + /** + * <p> + * Constructs an exception. + * </p> + * + * @param msg The error message + * + * @since 0.2.0 + */ + public SshException(String msg) { + super(msg); + } + + /** + * <p> + * Constructs an exception. + * </p> + * + * @param cause The cause of the exception + * + * @since 0.2.1 + */ + public SshException(Throwable cause) { + super(cause.getMessage()); + } +} diff --git a/src/com/sshtools/j2ssh/SshRuntimeException.java b/src/com/sshtools/j2ssh/SshRuntimeException.java new file mode 100644 index 0000000000000000000000000000000000000000..ad2143bf0e8210cd4b7534340cea889173f9adb7 --- /dev/null +++ b/src/com/sshtools/j2ssh/SshRuntimeException.java @@ -0,0 +1,52 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + + +/** + * <p> + * Runtime exception's thrown by the J2SSH application framework. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.14 $ + * + * @since 0.2.0 + */ +public class SshRuntimeException extends RuntimeException { + /** + * <p> + * Constructs a runtime exception. + * </p> + * + * @param msg the error message + * + * @since 0.2.0 + */ + public SshRuntimeException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/SshThread.java b/src/com/sshtools/j2ssh/SshThread.java new file mode 100644 index 0000000000000000000000000000000000000000..c0e55c402d8c3d7b1fdf8c893226fff5959a23d3 --- /dev/null +++ b/src/com/sshtools/j2ssh/SshThread.java @@ -0,0 +1,312 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; + +import java.util.HashMap; + + +/** + * <p> + * Enables the J2SSH application framework to execute threads in the context of + * a given session. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.25 $ + * + * @since 0.2.0 + */ +public class SshThread extends Thread { + private static HashMap names = new HashMap(); + + /** The raw session id generating during the first key exchange. */ + protected byte[] sessionId; + + /** A string representation of the session id. */ + protected String sessionIdString = null; + + /** The thread owner */ + protected String username; + + /** The thread properties */ + private HashMap settings = new HashMap(); + + /** + * <p> + * Constructs an SshThread. + * </p> + * + * @param target The target to execute + * @param name The name of the thread + * @param daemon run as a daemon thread? + * + * @since 0.2.0 + */ + public SshThread(Runnable target, String name, boolean daemon) { + super(target); + setProperties(name, daemon); + } + + public SshThread(String name, boolean daemon) { + setProperties(name, daemon); + } + + private void setProperties(String name, boolean daemon) { + Integer i; + + if (names.containsKey(name)) { + i = new Integer(((Integer) names.get(name)).intValue() + 1); + } else { + i = new Integer(1); + } + + names.put(name, i); + setName(name + " " + Integer.toHexString(i.intValue() & 0xFF)); + setDaemon(daemon); + + if (ConfigurationLoader.isContextClassLoader()) { + setContextClassLoader(ConfigurationLoader.getContextClassLoader()); + } + } + + /** + * <p> + * Sets the session id for this thread. + * </p> + * + * @param sessionId the session id created during the first key exchange. + * + * @since 0.2.0 + */ + public void setSessionId(byte[] sessionId) { + if (sessionId != null) { + this.sessionId = new byte[sessionId.length]; + System.arraycopy(sessionId, 0, this.sessionId, 0, sessionId.length); + sessionIdString = String.valueOf(new String(sessionId).hashCode() & + 0xFFFFFFFFL); + } + } + + /** + * <p> + * Returns the session id string for this thread. + * </p> + * + * @return a string representation of the session id + * + * @since 0.2.0 + */ + public String getSessionIdString() { + return sessionIdString; + } + + /** + * <p> + * Set the username for this thread. + * </p> + * + * @param username the thread owner + * + * @since 0.2.0 + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * <p> + * Gets the username for this thread. + * </p> + * + * @return the thread owner + * + * @since 0.2.0 + */ + public String getUsername() { + return username; + } + + /** + * <p> + * Create's a cloned copy of this thread with the given target and name. + * </p> + * + * @param target the target to execute + * @param name the thread name + * + * @return the cloned thread + * + * @since 0.2.0 + */ + public SshThread cloneThread(Runnable target, String name) { + SshThread thread = new SshThread(target, name, isDaemon()); + thread.setSessionId(sessionId); + thread.setUsername(username); + thread.settings.putAll(settings); + + return thread; + } + + /** + * <p> + * Sets a property in the thread. + * </p> + * + * @param name the name of the property + * @param value the property value + * + * @since 0.2.0 + */ + public void setProperty(String name, Object value) { + settings.put(name, value); + } + + /** + * <p> + * Gets a property from this thread. + * </p> + * + * @param name the name of the property + * + * @return the property value + * + * @since 0.2.0 + */ + public Object getProperty(String name) { + return settings.get(name); + } + + /** + * <p> + * Determine if this thread contains the given property. + * </p> + * + * @param name the name of the property + * + * @return true if the property exists, otherwise false + * + * @since 0.2.0 + */ + public boolean containsProperty(String name) { + return settings.containsKey(name); + } + + /** + * <p> + * Call to determine the username of the current thread context. + * </p> + * + * <p> + * This should be called when the caller is certain that the current thread + * is running in an <code>SshThread</code> context. If not a runtime + * exception is thrown. + * </p> + * + * @return the owner of the current thread + * + * @throws SshRuntimeException if the current thread is not an + * <code>SshThread</code> + * + * @since 0.2.0 + */ + public static String getCurrentThreadUser() throws SshRuntimeException { + String username; + + if (Thread.currentThread() instanceof SshThread) { + return ((SshThread) Thread.currentThread()).getUsername(); + } else { + throw new SshRuntimeException( + "The current thread is not running within an SshThread context"); + } + } + + public static boolean hasUserContext() { + if (Thread.currentThread() instanceof SshThread) { + return ((SshThread) Thread.currentThread()).getUsername() != null; + } else { + throw new SshRuntimeException( + "The current thread is not running within an SshThread context"); + } + } + + /** + * <p> + * Returns the session id of the current thread context. + * </p> + * + * <p> + * This should be called when the caller is certain that the current thread + * is running in an <code>SshThread</code> context. If not a Runtime + * exception is thrown. + * </p> + * + * @return the session id of the current thread + * + * @throws SshRuntimeException if the current thread is not an + * <code>SshThread</code> + * + * @since 0.2.0 + */ + public static String getCurrentSessionId() throws SshRuntimeException { + String username; + + if (Thread.currentThread() instanceof SshThread) { + return ((SshThread) Thread.currentThread()).getSessionIdString(); + } else { + throw new SshRuntimeException( + "The current thread is not running within an SshThread context"); + } + } + + /** + * <p> + * Returns the current <code>SshThread</code>. + * </p> + * + * <p> + * This should be called when the caller is certain that the current thread + * is running in an <code>SshThread</code> context. If not a Runtime + * exception is thrown. + * </p> + * + * @return the current <code>SshThread</code> + * + * @throws SshRuntimeException if the current thread is not an + * <code>SshThread</code> + * + * @since 0.2.0 + */ + public static SshThread getCurrentThread() throws SshRuntimeException { + if (Thread.currentThread() instanceof SshThread) { + return (SshThread) Thread.currentThread(); + } else { + throw new SshRuntimeException( + "The current thread is not an SshThread"); + } + } +} diff --git a/src/com/sshtools/j2ssh/TransferCancelledException.java b/src/com/sshtools/j2ssh/TransferCancelledException.java new file mode 100644 index 0000000000000000000000000000000000000000..064111b3f4cf2b3b8bb0cf179d96bbdc4d87f121 --- /dev/null +++ b/src/com/sshtools/j2ssh/TransferCancelledException.java @@ -0,0 +1,58 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh; + +import java.io.*; + + +/** + * <p> + * Title: + * </p> + * + * <p> + * Description: + * </p> + * + * <p> + * Copyright: Copyright (c) 2003 + * </p> + * + * <p> + * Company: + * </p> + * + * @author Lee David Painter + * @version $Id: TransferCancelledException.java,v 1.9 2003/09/11 15:35:00 martianx Exp $ + */ +public class TransferCancelledException extends IOException { + /** + * Creates a new TransferCancelledException object. + */ + public TransferCancelledException() { + super(); + } +} diff --git a/src/com/sshtools/j2ssh/agent/AgentAuthenticationClient.java b/src/com/sshtools/j2ssh/agent/AgentAuthenticationClient.java new file mode 100644 index 0000000000000000000000000000000000000000..848bca0099c40272c1000d30de778d4afc6922b7 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/AgentAuthenticationClient.java @@ -0,0 +1,282 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.SshClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolClient; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolException; +import com.sshtools.j2ssh.authentication.AuthenticationProtocolState; +import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshAuthenticationClient; +import com.sshtools.j2ssh.authentication.SshMsgUserAuthPKOK; +import com.sshtools.j2ssh.authentication.SshMsgUserAuthRequest; +import com.sshtools.j2ssh.authentication.TerminatedStateException; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.SshMessage; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.Component; + +import java.io.IOException; + +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + + +/** + * <p> + * Provides an application with an authentication mechanism that links to the + * sshtools agent; the agent stores private keys and can hash and sign data + * for the public key authentication request. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.19 $ + */ +public class AgentAuthenticationClient extends SshAuthenticationClient { + private static Log log = LogFactory.getLog(PublicKeyAuthenticationClient.class); + + /** */ + protected SshAgentClient agent; + + /** + * Creates a new AgentAuthenticationClient object. + */ + public AgentAuthenticationClient() { + } + + /*public void setKey(SshPublicKey key) { + this.key = key; + }*/ + public void setAgent(SshAgentClient agent) { + this.agent = agent; + } + + /** + * + */ + public void reset() { + agent = null; + } + + /** + * + * + * @return + */ + public String getMethodName() { + return "publickey"; + } + + /** + * + * + * @param authentication + * @param username + * @param serviceToStart + * @param key + * + * @return + * + * @throws IOException + */ + public boolean acceptsKey(AuthenticationProtocolClient authentication, + String username, String serviceToStart, SshPublicKey key) + throws IOException { + authentication.registerMessage(SshMsgUserAuthPKOK.class, + SshMsgUserAuthPKOK.SSH_MSG_USERAUTH_PK_OK); + log.info( + "Determining if server can accept public key for authentication"); + + ByteArrayWriter baw = new ByteArrayWriter(); + + // Now prepare and send the message + baw.write(0); + baw.writeString(key.getAlgorithmName()); + baw.writeBinaryString(key.getEncoded()); + + SshMessage msg = new SshMsgUserAuthRequest(username, serviceToStart, + getMethodName(), baw.toByteArray()); + authentication.sendMessage(msg); + + try { + msg = authentication.readMessage(SshMsgUserAuthPKOK.SSH_MSG_USERAUTH_PK_OK); + + if (msg instanceof SshMsgUserAuthPKOK) { + return true; + } else { + throw new IOException( + "Unexpected message returned from readMessage"); + } + } catch (TerminatedStateException ex) { + return false; + } + } + + /** + * + * + * @param authentication + * @param serviceToStart + * + * @throws IOException + * @throws TerminatedStateException + * @throws AuthenticationProtocolException + */ + public void authenticate(AuthenticationProtocolClient authentication, + String serviceToStart) throws IOException, TerminatedStateException { + if ((getUsername() == null) || (agent == null)) { + throw new AuthenticationProtocolException( + "You must supply a username and agent"); + } + + // Iterate the agents keys, find an acceptable key and authenticate + Map keys = agent.listKeys(); + Iterator it = keys.entrySet().iterator(); + boolean acceptable = false; + SshPublicKey key = null; + String description; + Map.Entry entry; + + while (it.hasNext() && !acceptable) { + entry = (Map.Entry) it.next(); + key = (SshPublicKey) entry.getKey(); + description = (String) entry.getValue(); + acceptable = acceptsKey(authentication, getUsername(), + serviceToStart, key); + log.info("Agent authentication with key " + key.getFingerprint() + + " [" + description + "] is " + + (acceptable ? " acceptable" : " not acceptable")); + + if (acceptable) { + ByteArrayWriter baw = new ByteArrayWriter(); + log.info("Generating data to sign"); + log.info("Preparing public key authentication request"); + + // Now prepare and send the message + baw.write(1); + baw.writeString(key.getAlgorithmName()); + baw.writeBinaryString(key.getEncoded()); + + // Create the signature data + ByteArrayWriter data = new ByteArrayWriter(); + data.writeBinaryString(authentication.getSessionIdentifier()); + data.write(SshMsgUserAuthRequest.SSH_MSG_USERAUTH_REQUEST); + data.writeString(getUsername()); + data.writeString(serviceToStart); + data.writeString(getMethodName()); + data.write(1); + data.writeString(key.getAlgorithmName()); + data.writeBinaryString(key.getEncoded()); + + // Generate the signature + baw.writeBinaryString(agent.hashAndSign(key, data.toByteArray())); + + SshMsgUserAuthRequest msg = new SshMsgUserAuthRequest(getUsername(), + serviceToStart, getMethodName(), baw.toByteArray()); + authentication.sendMessage(msg); + + try { + authentication.readAuthenticationState(); + } catch (TerminatedStateException ex) { + if (ex.getState() == AuthenticationProtocolState.COMPLETE) { + throw ex; + } + } + } + } + + throw new TerminatedStateException(AuthenticationProtocolState.FAILED); + } + + /** + * + * + * @param parent + * + * @return + */ + public boolean showAuthenticationDialog(Component parent) { + return false; + } + + /** + * + * + * @return + */ + public Properties getPersistableProperties() { + Properties properties = new Properties(); + + return properties; + } + + /** + * + * + * @param properties + */ + public void setPersistableProperties(Properties properties) { + } + + /** + * + * + * @return + */ + public boolean canAuthenticate() { + return ((agent != null) && (getUsername() != null)); + } + + /** + * + * + * @param ssh + * + * @return + */ + public boolean hasAcceptableKey(SshClient ssh) { + try { + Map keys = agent.listKeys(); + SshPublicKey key; + + for (Iterator x = keys.keySet().iterator(); x.hasNext();) { + key = (SshPublicKey) x.next(); + + if (ssh.acceptsKey(getUsername(), key)) { + return true; + } + } + } catch (IOException ex) { + } + + return false; + } +} diff --git a/src/com/sshtools/j2ssh/agent/AgentNotAvailableException.java b/src/com/sshtools/j2ssh/agent/AgentNotAvailableException.java new file mode 100644 index 0000000000000000000000000000000000000000..34b674c0b9ba04823a5a387da348b07beeb23a4a --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/AgentNotAvailableException.java @@ -0,0 +1,42 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class AgentNotAvailableException extends Exception { + /** + * Creates a new AgentNotAvailableException object. + */ + public AgentNotAvailableException() { + super("An agent could not be found"); + } +} diff --git a/src/com/sshtools/j2ssh/agent/AgentSocketChannel.java b/src/com/sshtools/j2ssh/agent/AgentSocketChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..c72dc106efb7a6f6b2b5eb5f1e3f4abd77d382c3 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/AgentSocketChannel.java @@ -0,0 +1,158 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.connection.InvalidChannelException; +import com.sshtools.j2ssh.connection.SocketChannel; +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import java.io.IOException; + +import java.net.InetAddress; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class AgentSocketChannel extends SocketChannel { + /** */ + public static final String AGENT_FORWARDING_CHANNEL = "auth-agent"; + + //protected Socket socket = null; + private boolean isForwarding; + + /** + * Creates a new AgentSocketChannel object. + * + * @param isForwarding + */ + public AgentSocketChannel(boolean isForwarding) { + this.isForwarding = isForwarding; + } + + /** + * + * + * @return + */ + public String getChannelType() { + return AGENT_FORWARDING_CHANNEL; + } + + /*public void bindSocket(Socket socket) throws IOException { + this.socket = socket; + if (state.getValue() == ChannelState.CHANNEL_OPEN) { + bindInputStream(socket.getInputStream()); + bindOutputStream(socket.getOutputStream()); + } + }*/ + protected void onChannelRequest(String requestType, boolean wantReply, + byte[] requestData) throws java.io.IOException { + if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + + /** + * + * + * @return + */ + protected int getMaximumPacketSize() { + return 32678; + } + + /*protected void onChannelClose() throws java.io.IOException { + } + protected void onChannelEOF() throws IOException { + }*/ + public byte[] getChannelOpenData() { + return null; + } + + /** + * + * + * @return + */ + protected int getMinimumWindowSpace() { + return 1024; + } + + /** + * + * + * @throws com.sshtools.j2ssh.connection.InvalidChannelException DOCUMENT + * ME! + * @throws InvalidChannelException + */ + protected void onChannelOpen() + throws com.sshtools.j2ssh.connection.InvalidChannelException { + try { + //if (socket != null) { + if (isForwarding) { + // Were forwarding so insert the forwarding notice before any other data + SshAgentForwardingNotice msg = new SshAgentForwardingNotice(InetAddress.getLocalHost() + .getHostName(), + InetAddress.getLocalHost().getHostAddress(), + socket.getPort()); + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeBinaryString(msg.toByteArray()); + sendChannelData(baw.toByteArray()); + } + + super.onChannelOpen(); + + // Now bind the socket to the channel + // bindInputStream(socket.getInputStream()); + // bindOutputStream(socket.getOutputStream()); + //} + } catch (IOException ex) { + throw new InvalidChannelException(ex.getMessage()); + } + } + + /** + * + * + * @return + */ + protected int getMaximumWindowSpace() { + return 32768; + } + + /** + * + * + * @return + */ + public byte[] getChannelConfirmationData() { + return null; + } +} diff --git a/src/com/sshtools/j2ssh/agent/ForwardingNotice.java b/src/com/sshtools/j2ssh/agent/ForwardingNotice.java new file mode 100644 index 0000000000000000000000000000000000000000..92dcefc40d76470c6d1da0cd90f5ff8bfbc7f423 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/ForwardingNotice.java @@ -0,0 +1,73 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +class ForwardingNotice { + String remoteHostname; + String remoteIPAddress; + int remotePort; + + /** + * Creates a new ForwardingNotice object. + * + * @param remoteHostname + * @param remoteIPAddress + * @param remotePort + */ + public ForwardingNotice(String remoteHostname, String remoteIPAddress, + int remotePort) { + this.remoteHostname = remoteHostname; + this.remoteIPAddress = remoteIPAddress; + this.remotePort = remotePort; + } + + /** + * + * + * @return + */ + public String getRemoteHostname() { + return remoteHostname; + } + + /** + * + * + * @return + */ + public String getRemoteIPAddress() { + return remoteIPAddress; + } + + /** + * + * + * @return + */ + public int getRemotePort() { + return remotePort; + } +} diff --git a/src/com/sshtools/j2ssh/agent/KeyConstraints.java b/src/com/sshtools/j2ssh/agent/KeyConstraints.java new file mode 100644 index 0000000000000000000000000000000000000000..1e218869daf3d53115c5b5635e2b3672c7a6408f --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/KeyConstraints.java @@ -0,0 +1,292 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class KeyConstraints { + /** */ + public static final long NO_TIMEOUT = 0; + + /** */ + public static final long NO_LIMIT = 0xffffffffL; + + /** */ + protected static final int SSH_AGENT_CONSTRAINT_TIMEOUT = 50; + + /** */ + protected static final int SSH_AGENT_CONSTRAINT_USE_LIMIT = 51; + + /** */ + protected static final int SSH_AGENT_CONSTRAINT_FORWARDING_STEPS = 52; + + /** */ + protected static final int SSH_AGENT_CONSTRAINT_FORWARDING_PATH = 100; + + /** */ + protected static final int SSH_AGENT_CONSTRAINT_SSH1_COMPAT = 150; + + /** */ + protected static final int SSH_AGENT_CONSTRAINT_NEED_USER_VERIFICATION = 151; + private UnsignedInteger32 timeout = new UnsignedInteger32(NO_TIMEOUT); + private UnsignedInteger32 uselimit = new UnsignedInteger32(NO_LIMIT); + private UnsignedInteger32 maxsteps = new UnsignedInteger32(NO_LIMIT); + private String forwardingpath = ""; + private boolean userverify = false; + private boolean compat = false; + private long keyadded = System.currentTimeMillis(); + private long usedcount = 0; + + /** + * Creates a new KeyConstraints object. + */ + public KeyConstraints() { + } + + /** + * Creates a new KeyConstraints object. + * + * @param bar + * + * @throws IOException + */ + public KeyConstraints(ByteArrayReader bar) throws IOException { + while (bar.available() > 0) { + switch (bar.read() & 0xFF) { + case SSH_AGENT_CONSTRAINT_TIMEOUT: + timeout = bar.readUINT32(); + + break; + + case SSH_AGENT_CONSTRAINT_USE_LIMIT: + uselimit = bar.readUINT32(); + + break; + + case SSH_AGENT_CONSTRAINT_FORWARDING_STEPS: + maxsteps = bar.readUINT32(); + + break; + + case SSH_AGENT_CONSTRAINT_FORWARDING_PATH: + forwardingpath = bar.readString(); + + break; + + case SSH_AGENT_CONSTRAINT_SSH1_COMPAT: + compat = (bar.read() != 0); + + break; + + case SSH_AGENT_CONSTRAINT_NEED_USER_VERIFICATION: + userverify = (bar.read() != 0); + + break; + } + } + } + + /** + * + * + * @param timeout + */ + public void setKeyTimeout(UnsignedInteger32 timeout) { + this.timeout = timeout; + } + + /** + * + * + * @param uselimit + */ + public void setKeyUseLimit(int uselimit) { + this.uselimit = new UnsignedInteger32(uselimit); + } + + /** + * + * + * @param maxsteps + */ + public void setMaximumForwardingSteps(int maxsteps) { + this.maxsteps = new UnsignedInteger32(maxsteps); + } + + /** + * + * + * @param forwardingpath + */ + public void setForwardingPath(String forwardingpath) { + this.forwardingpath = forwardingpath; + } + + /** + * + * + * @param userverify + */ + public void setRequiresUserVerification(boolean userverify) { + this.userverify = userverify; + } + + /** + * + * + * @param compat + */ + public void setSSH1Compatible(boolean compat) { + this.compat = compat; + } + + /** + * + * + * @return + */ + public long getKeyTimeout() { + return timeout.longValue(); + } + + /** + * + * + * @return + */ + public long getKeyUseLimit() { + return uselimit.longValue(); + } + + /** + * + * + * @return + */ + public long getMaximumForwardingSteps() { + return maxsteps.longValue(); + } + + /** + * + * + * @return + */ + public long getUsedCount() { + return usedcount; + } + + /** + * + * + * @return + */ + public boolean hasTimedOut() { + return (timeout.longValue() != 0) + ? (((System.currentTimeMillis() - keyadded) / 1000) > timeout.longValue()) + : false; + } + + /** + * + * + * @return + */ + public boolean canUse() { + return (uselimit.longValue() != 0) ? (usedcount < uselimit.longValue()) + : true; + } + + /** + * + */ + public void use() { + usedcount++; + } + + /** + * + * + * @return + */ + public String getForwardingPath() { + return forwardingpath; + } + + /** + * + * + * @return + */ + public boolean requiresUserVerification() { + return userverify; + } + + /** + * + * + * @return + */ + public boolean isSSH1Compatible() { + return compat; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public byte[] toByteArray() throws IOException { + ByteArrayWriter baw = new ByteArrayWriter(); + baw.write(SSH_AGENT_CONSTRAINT_TIMEOUT); + baw.writeUINT32(timeout); + baw.write(SSH_AGENT_CONSTRAINT_USE_LIMIT); + baw.writeUINT32(uselimit); + baw.write(SSH_AGENT_CONSTRAINT_FORWARDING_STEPS); + baw.writeUINT32(maxsteps); + baw.write(SSH_AGENT_CONSTRAINT_FORWARDING_PATH); + baw.writeString(forwardingpath); + baw.write(SSH_AGENT_CONSTRAINT_SSH1_COMPAT); + baw.write(compat ? 0 : 1); + baw.write(SSH_AGENT_CONSTRAINT_NEED_USER_VERIFICATION); + baw.write(userverify ? 0 : 1); + + return baw.toByteArray(); + } +} diff --git a/src/com/sshtools/j2ssh/agent/KeyStore.java b/src/com/sshtools/j2ssh/agent/KeyStore.java new file mode 100644 index 0000000000000000000000000000000000000000..470531b7425e1229d72bc873d9d34590266d1361 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/KeyStore.java @@ -0,0 +1,339 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.transport.publickey.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class KeyStore { + private static Log log = LogFactory.getLog(KeyStore.class); + HashMap publickeys = new HashMap(); + HashMap privatekeys = new HashMap(); + HashMap constraints = new HashMap(); + Vector index = new Vector(); + Vector listeners = new Vector(); + String lockedPassword = null; + + /** + * Creates a new KeyStore object. + */ + public KeyStore() { + } + + /** + * + * + * @return + */ + public Map getPublicKeys() { + return (Map) publickeys.clone(); + } + + /** + * + * + * @param key + * + * @return + */ + public int indexOf(SshPublicKey key) { + return index.indexOf(key); + } + + /** + * + * + * @param i + * + * @return + */ + public SshPublicKey elementAt(int i) { + return (SshPublicKey) index.elementAt(i); + } + + /** + * + * + * @param key + * + * @return + */ + public String getDescription(SshPublicKey key) { + return (String) publickeys.get(key); + } + + /** + * + * + * @param key + * + * @return + */ + public KeyConstraints getKeyConstraints(SshPublicKey key) { + return (KeyConstraints) constraints.get(key); + } + + /** + * + * + * @return + */ + public int size() { + return index.size(); + } + + /** + * + * + * @param listener + */ + public void addKeyStoreListener(KeyStoreListener listener) { + listeners.add(listener); + } + + /** + * + * + * @param listener + */ + public void removeKeyStoreListener(KeyStoreListener listener) { + listeners.remove(listener); + } + + /** + * + * + * @param prvkey + * @param pubkey + * @param description + * @param cs + * + * @return + * + * @throws IOException + */ + public boolean addKey(SshPrivateKey prvkey, SshPublicKey pubkey, + String description, KeyConstraints cs) throws IOException { + synchronized (publickeys) { + if (!publickeys.containsKey(pubkey)) { + publickeys.put(pubkey, description); + privatekeys.put(pubkey, prvkey); + constraints.put(pubkey, cs); + index.add(pubkey); + + Iterator it = listeners.iterator(); + KeyStoreListener listener; + + while (it.hasNext()) { + listener = (KeyStoreListener) it.next(); + listener.onAddKey(this); + } + + return true; + } else { + return false; + } + } + } + + /** + * + */ + public void deleteAllKeys() { + synchronized (publickeys) { + publickeys.clear(); + privatekeys.clear(); + constraints.clear(); + index.clear(); + + Iterator it = listeners.iterator(); + KeyStoreListener listener; + + while (it.hasNext()) { + listener = (KeyStoreListener) it.next(); + listener.onDeleteAllKeys(this); + } + } + } + + /** + * + * + * @param pubkey + * @param forwardingNodes + * @param data + * + * @return + * + * @throws KeyTimeoutException + * @throws InvalidSshKeyException + * @throws InvalidSshKeySignatureException + */ + public byte[] performHashAndSign(SshPublicKey pubkey, List forwardingNodes, + byte[] data) + throws KeyTimeoutException, InvalidSshKeyException, + InvalidSshKeySignatureException { + synchronized (publickeys) { + if (privatekeys.containsKey(pubkey)) { + SshPrivateKey key = (SshPrivateKey) privatekeys.get(pubkey); + KeyConstraints cs = (KeyConstraints) constraints.get(pubkey); + + if (cs.canUse()) { + if (!cs.hasTimedOut()) { + cs.use(); + + byte[] sig = key.generateSignature(data); + Iterator it = listeners.iterator(); + KeyStoreListener listener; + + while (it.hasNext()) { + listener = (KeyStoreListener) it.next(); + listener.onKeyOperation(this, "hash-and-sign"); + } + + return sig; + } else { + throw new KeyTimeoutException(); + } + } else { + throw new KeyTimeoutException(); + } + } else { + throw new InvalidSshKeyException("The key does not exist"); + } + } + } + + /** + * + * + * @param pubkey + * @param description + * + * @return + * + * @throws IOException + */ + public boolean deleteKey(SshPublicKey pubkey, String description) + throws IOException { + synchronized (publickeys) { + if (publickeys.containsKey(pubkey)) { + String desc = (String) publickeys.get(pubkey); + + if (description.equals(desc)) { + publickeys.remove(pubkey); + privatekeys.remove(pubkey); + constraints.remove(pubkey); + index.remove(pubkey); + + Iterator it = listeners.iterator(); + KeyStoreListener listener; + + while (it.hasNext()) { + listener = (KeyStoreListener) it.next(); + listener.onDeleteKey(this); + } + + return true; + } + } + + return false; + } + } + + /** + * + * + * @param password + * + * @return + * + * @throws IOException + */ + public boolean lock(String password) throws IOException { + synchronized (publickeys) { + if (lockedPassword == null) { + lockedPassword = password; + + Iterator it = listeners.iterator(); + KeyStoreListener listener; + + while (it.hasNext()) { + listener = (KeyStoreListener) it.next(); + listener.onLock(this); + } + + return true; + } else { + return false; + } + } + } + + /** + * + * + * @param password + * + * @return + * + * @throws IOException + */ + public boolean unlock(String password) throws IOException { + synchronized (publickeys) { + if (lockedPassword != null) { + if (password.equals(lockedPassword)) { + lockedPassword = null; + + Iterator it = listeners.iterator(); + KeyStoreListener listener; + + while (it.hasNext()) { + listener = (KeyStoreListener) it.next(); + listener.onUnlock(this); + } + + return true; + } + } + + return false; + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/KeyStoreListener.java b/src/com/sshtools/j2ssh/agent/KeyStoreListener.java new file mode 100644 index 0000000000000000000000000000000000000000..ad72c27311252a1e413ec090989b74343220d0b1 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/KeyStoreListener.java @@ -0,0 +1,78 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface KeyStoreListener { + /** + * + * + * @param keystore + */ + public void onDeleteKey(KeyStore keystore); + + /** + * + * + * @param keystore + */ + public void onLock(KeyStore keystore); + + /** + * + * + * @param keystore + */ + public void onUnlock(KeyStore keystore); + + /** + * + * + * @param keystore + */ + public void onAddKey(KeyStore keystore); + + /** + * + * + * @param keystore + */ + public void onDeleteAllKeys(KeyStore keystore); + + /** + * + * + * @param keystore + * @param operation + */ + public void onKeyOperation(KeyStore keystore, String operation); +} diff --git a/src/com/sshtools/j2ssh/agent/KeyTimeoutException.java b/src/com/sshtools/j2ssh/agent/KeyTimeoutException.java new file mode 100644 index 0000000000000000000000000000000000000000..907ccc2eab7a18fd418c017e8d6c192e21af9a77 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/KeyTimeoutException.java @@ -0,0 +1,42 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class KeyTimeoutException extends Exception { + /** + * Creates a new KeyTimeoutException object. + */ + public KeyTimeoutException() { + super("The key has timed out"); + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentAddKey.java b/src/com/sshtools/j2ssh/agent/SshAgentAddKey.java new file mode 100644 index 0000000000000000000000000000000000000000..cd83642eec485b115e2cebc4be4a374aae5a641e --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentAddKey.java @@ -0,0 +1,161 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.IOException; + + +class SshAgentAddKey extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_ADD_KEY = 202; + SshPrivateKey prvkey; + SshPublicKey pubkey; + String description; + KeyConstraints constraints; + + /** + * Creates a new SshAgentAddKey object. + */ + public SshAgentAddKey() { + super(SSH_AGENT_ADD_KEY); + } + + /** + * Creates a new SshAgentAddKey object. + * + * @param prvkey + * @param pubkey + * @param description + * @param constraints + */ + public SshAgentAddKey(SshPrivateKey prvkey, SshPublicKey pubkey, + String description, KeyConstraints constraints) { + super(SSH_AGENT_ADD_KEY); + this.prvkey = prvkey; + this.pubkey = pubkey; + this.description = description; + this.constraints = constraints; + } + + /** + * + * + * @return + */ + public SshPrivateKey getPrivateKey() { + return prvkey; + } + + /** + * + * + * @return + */ + public SshPublicKey getPublicKey() { + return pubkey; + } + + /** + * + * + * @return + */ + public String getDescription() { + return description; + } + + /** + * + * + * @return + */ + public KeyConstraints getKeyConstraints() { + return constraints; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_ADD_KEY"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeBinaryString(prvkey.getEncoded()); + baw.writeBinaryString(pubkey.getEncoded()); + baw.writeString(description); + baw.write(constraints.toByteArray()); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + prvkey = SshKeyPairFactory.decodePrivateKey(bar.readBinaryString()); + pubkey = SshKeyPairFactory.decodePublicKey(bar.readBinaryString()); + description = bar.readString(); + constraints = new KeyConstraints(bar); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentAlive.java b/src/com/sshtools/j2ssh/agent/SshAgentAlive.java new file mode 100644 index 0000000000000000000000000000000000000000..583f690a8024acd3ae19a806a5160ffe2b54cf2e --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentAlive.java @@ -0,0 +1,116 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentAlive extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_ALIVE = 150; + private byte[] padding; + + /** + * Creates a new SshAgentAlive object. + */ + public SshAgentAlive() { + super(SSH_AGENT_ALIVE); + } + + /** + * Creates a new SshAgentAlive object. + * + * @param padding + */ + public SshAgentAlive(byte[] padding) { + super(SSH_AGENT_ALIVE); + this.padding = padding; + } + + /** + * + * + * @return + */ + public byte[] getPadding() { + return padding; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_ALIVE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.write(padding); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + padding = new byte[bar.available()]; + bar.read(padding); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentClient.java b/src/com/sshtools/j2ssh/agent/SshAgentClient.java new file mode 100644 index 0000000000000000000000000000000000000000..611aafba34537a4b34ff7068894fe082bb5e30c5 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentClient.java @@ -0,0 +1,479 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.InetAddress; +import java.net.Socket; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + + +/** + * Provides a client connection to the ssh agent. + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshAgentClient { + private static Log log = LogFactory.getLog(SshAgentClient.class); + + /** The hash and sign private key operation */ + public static final String HASH_AND_SIGN = "hash-and-sign"; + InputStream in; + OutputStream out; + boolean isForwarded = false; + HashMap messages = new HashMap(); + Socket socket; + + SshAgentClient(boolean isForwarded, String application, InputStream in, + OutputStream out) throws IOException { + log.info("New SshAgentClient instance created"); + this.in = in; + this.out = out; + this.isForwarded = isForwarded; + registerMessages(); + + if (isForwarded) { + sendForwardingNotice(); + } else { + sendVersionRequest(application); + } + } + + SshAgentClient(boolean isForwarded, String application, Socket socket) + throws IOException { + log.info("New SshAgentClient instance created"); + this.socket = socket; + this.in = socket.getInputStream(); + this.out = socket.getOutputStream(); + this.isForwarded = isForwarded; + registerMessages(); + + if (isForwarded) { + sendForwardingNotice(); + } else { + sendVersionRequest(application); + } + } + + /** + * Connect to the local agent. + * + * @param application the application connecting + * @param location the location of the agent, in the form "localhost:port" + * + * @return a connected agent client + * + * @throws AgentNotAvailableException if the agent is not available at the + * location specified + * @throws IOException if an IO error occurs + */ + public static SshAgentClient connectLocalAgent(String application, + String location) throws AgentNotAvailableException, IOException { + try { + Socket socket = connectAgentSocket(location); + + return new SshAgentClient(false, application, socket); + } catch (IOException ex) { + throw new AgentNotAvailableException(); + } + } + + /** + * Connect a socket to the agent at the location specified. + * + * @param location the location of the agent, in the form "localhost:port" + * + * @return the connected socket + * + * @throws AgentNotAvailableException if an agent is not available at the + * location specified + * @throws IOException if an IO error occurs + */ + public static Socket connectAgentSocket(String location) + throws AgentNotAvailableException, IOException { + try { + if (location == null) { + throw new AgentNotAvailableException(); + } + + int idx = location.indexOf(":"); + + if (idx == -1) { + throw new AgentNotAvailableException(); + } + + String host = location.substring(0, idx); + int port = Integer.parseInt(location.substring(idx + 1)); + Socket socket = new Socket(host, port); + + return socket; + } catch (IOException ex) { + throw new AgentNotAvailableException(); + } + } + + /** + * Close the agent + */ + public void close() { + log.info("Closing agent client"); + + try { + in.close(); + } catch (IOException ex) { + } + + try { + out.close(); + } catch (IOException ex1) { + } + + try { + if (socket != null) { + socket.close(); + } + } catch (IOException ex2) { + } + } + + /** + * Register the subsystem messages + */ + protected void registerMessages() { + messages.put(new Integer( + SshAgentVersionResponse.SSH_AGENT_VERSION_RESPONSE), + SshAgentVersionResponse.class); + messages.put(new Integer(SshAgentSuccess.SSH_AGENT_SUCCESS), + SshAgentSuccess.class); + messages.put(new Integer(SshAgentFailure.SSH_AGENT_FAILURE), + SshAgentFailure.class); + messages.put(new Integer(SshAgentKeyList.SSH_AGENT_KEY_LIST), + SshAgentKeyList.class); + messages.put(new Integer(SshAgentRandomData.SSH_AGENT_RANDOM_DATA), + SshAgentRandomData.class); + messages.put(new Integer(SshAgentAlive.SSH_AGENT_ALIVE), + SshAgentAlive.class); + messages.put(new Integer( + SshAgentOperationComplete.SSH_AGENT_OPERATION_COMPLETE), + SshAgentOperationComplete.class); + } + + /** + * Request the agent version. + * + * @param application the application connecting + * + * @throws IOException if an IO error occurs + */ + protected void sendVersionRequest(String application) + throws IOException { + SubsystemMessage msg = new SshAgentRequestVersion(application); + sendMessage(msg); + msg = readMessage(); + + if (msg instanceof SshAgentVersionResponse) { + SshAgentVersionResponse reply = (SshAgentVersionResponse) msg; + + if (reply.getVersion() != 2) { + throw new IOException( + "The agent verison is not compatible with verison 2"); + } + } else { + throw new IOException( + "The agent did not respond with the appropriate version"); + } + } + + /** + * Add a key to the agent + * + * @param prvkey the private key to add + * @param pubkey the private keys public key + * @param description a description of the key + * @param constraints a set of contraints for key use + * + * @throws IOException if an IO error occurs + */ + public void addKey(SshPrivateKey prvkey, SshPublicKey pubkey, + String description, KeyConstraints constraints) + throws IOException { + SubsystemMessage msg = new SshAgentAddKey(prvkey, pubkey, description, + constraints); + sendMessage(msg); + msg = readMessage(); + + if (!(msg instanceof SshAgentSuccess)) { + throw new IOException("The key could not be added"); + } + } + + /** + * Request a hash and sign operation be performed for a given public key. + * + * @param key the public key of the required private key + * @param data the data to has and sign + * + * @return the hashed and signed data + * + * @throws IOException if an IO error occurs + */ + public byte[] hashAndSign(SshPublicKey key, byte[] data) + throws IOException { + SubsystemMessage msg = new SshAgentPrivateKeyOp(key, HASH_AND_SIGN, data); + sendMessage(msg); + msg = readMessage(); + + if (msg instanceof SshAgentOperationComplete) { + return ((SshAgentOperationComplete) msg).getData(); + } else { + throw new IOException("The operation failed"); + } + } + + /** + * List all the keys on the agent. + * + * @return a map of public keys and descriptions + * + * @throws IOException if an IO error occurs + */ + public Map listKeys() throws IOException { + SubsystemMessage msg = new SshAgentListKeys(); + sendMessage(msg); + msg = readMessage(); + + if (msg instanceof SshAgentKeyList) { + return ((SshAgentKeyList) msg).getKeys(); + } else { + throw new IOException("The agent responsed with an invalid message"); + } + } + + /** + * Lock the agent + * + * @param password password that will be required to unlock + * + * @return true if the agent was locked, otherwise false + * + * @throws IOException if an IO error occurs + */ + public boolean lockAgent(String password) throws IOException { + SubsystemMessage msg = new SshAgentLock(password); + sendMessage(msg); + msg = readMessage(); + + return (msg instanceof SshAgentSuccess); + } + + /** + * Unlock the agent + * + * @param password the password to unlock + * + * @return true if the agent was unlocked, otherwise false + * + * @throws IOException if an IO error occurs + */ + public boolean unlockAgent(String password) throws IOException { + SubsystemMessage msg = new SshAgentUnlock(password); + sendMessage(msg); + msg = readMessage(); + + return (msg instanceof SshAgentSuccess); + } + + /** + * Request some random data from the remote side + * + * @param count the number of bytes needed + * + * @return the random data received + * + * @throws IOException if an IO error occurs + */ + public byte[] getRandomData(int count) throws IOException { + SubsystemMessage msg = new SshAgentRandom(count); + sendMessage(msg); + msg = readMessage(); + + if (msg instanceof SshAgentRandomData) { + return ((SshAgentRandomData) msg).getRandomData(); + } else { + throw new IOException( + "Agent failed to provide the request random data"); + } + } + + /** + * Ping the remote side with some random padding data + * + * @param padding the padding data + * + * @throws IOException if an IO error occurs + */ + public void ping(byte[] padding) throws IOException { + SubsystemMessage msg = new SshAgentPing(padding); + sendMessage(msg); + msg = readMessage(); + + if (msg instanceof SshAgentAlive) { + if (!Arrays.equals(padding, ((SshAgentAlive) msg).getPadding())) { + throw new IOException( + "Agent failed to reply with expected data"); + } + } else { + throw new IOException( + "Agent failed to provide the request random data"); + } + } + + /** + * Delete a key held by the agent + * + * @param key the public key of the private key to delete + * @param description the description of the key + * + * @throws IOException if an IO error occurs + */ + public void deleteKey(SshPublicKey key, String description) + throws IOException { + SubsystemMessage msg = new SshAgentDeleteKey(key, description); + sendMessage(msg); + msg = readMessage(); + + if (!(msg instanceof SshAgentSuccess)) { + throw new IOException("The agent failed to delete the key"); + } + } + + /** + * Delete all the keys held by the agent. + * + * @throws IOException if an IO error occurs + */ + public void deleteAllKeys() throws IOException { + SubsystemMessage msg = new SshAgentDeleteAllKeys(); + sendMessage(msg); + msg = readMessage(); + + if (!(msg instanceof SshAgentSuccess)) { + throw new IOException("The agent failed to delete all keys"); + } + } + + /** + * Send a forwarding notice. + * + * @throws IOException if an IO error occurs + */ + protected void sendForwardingNotice() throws IOException { + InetAddress addr = InetAddress.getLocalHost(); + SshAgentForwardingNotice msg = new SshAgentForwardingNotice(addr.getHostName(), + addr.getHostAddress(), 22); + sendMessage(msg); + } + + /** + * Send a subsystem message + * + * @param msg the message to send + * + * @throws IOException if an IO error occurs + */ + protected void sendMessage(SubsystemMessage msg) throws IOException { + log.info("Sending message " + msg.getMessageName()); + + byte[] msgdata = msg.toByteArray(); + out.write(ByteArrayWriter.encodeInt(msgdata.length)); + out.write(msgdata); + out.flush(); + } + + /** + * Read a single message from the inputstream and convert into a valid + * subsystem message + * + * @return the next available subsystem message + * + * @throws InvalidMessageException if the message received is invalid + */ + protected SubsystemMessage readMessage() throws InvalidMessageException { + try { + byte[] lendata = new byte[4]; + byte[] msgdata; + int len; + + // Read the first 4 bytes to determine the length of the message + len = 0; + + while (len < 3) { + len += in.read(lendata, len, lendata.length - len); + } + + len = (int) ByteArrayReader.readInt(lendata, 0); + msgdata = new byte[len]; + len = 0; + + while (len < msgdata.length) { + len += in.read(msgdata, len, msgdata.length - len); + } + + Integer id = new Integer((int) msgdata[0] & 0xFF); + + if (messages.containsKey(id)) { + Class cls = (Class) messages.get(id); + SubsystemMessage msg = (SubsystemMessage) cls.newInstance(); + msg.fromByteArray(msgdata); + log.info("Received message " + msg.getMessageName()); + + return msg; + } else { + throw new InvalidMessageException("Unrecognised message id " + + id.toString()); + } + } catch (Exception ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentConnection.java b/src/com/sshtools/j2ssh/agent/SshAgentConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..e49958dbef7d1a409e72706212a07588ae31cbcb --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentConnection.java @@ -0,0 +1,527 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.configuration.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.subsystem.*; +import com.sshtools.j2ssh.transport.publickey.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import java.net.*; + +import java.util.*; + + +/** + * This class provides a connection using the SSH agent protocol. + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class SshAgentConnection implements Runnable { + private static Log log = LogFactory.getLog(SshAgentConnection.class); + InputStream in; + OutputStream out; + KeyStore keystore; + Thread thread; + Vector forwardingNodes = new Vector(); + Socket socket; + + SshAgentConnection(KeyStore keystore, InputStream in, OutputStream out) { + this.keystore = keystore; + this.in = in; + this.out = out; + thread = new Thread(this); + thread.start(); + } + + SshAgentConnection(KeyStore keystore, Socket socket) + throws IOException { + this.keystore = keystore; + this.in = socket.getInputStream(); + this.out = socket.getOutputStream(); + this.socket = socket; + socket.setSoTimeout(5000); + thread = new Thread(this); + thread.start(); + } + + /** + * Send a success message. + * + * @throws IOException if an IO error occurs + */ + protected void sendAgentSuccess() throws IOException { + SshAgentSuccess msg = new SshAgentSuccess(); + sendMessage(msg); + } + + /** + * Send a failure message + * + * @param errorcode the error code of the failure + * + * @throws IOException if an IO error occurs + */ + protected void sendAgentFailure(int errorcode) throws IOException { + SshAgentFailure msg = new SshAgentFailure(errorcode); + sendMessage(msg); + } + + /** + * Send the version response; this class currently implements version 2 + * + * @throws IOException if an IO error occurs + */ + protected void sendVersionResponse() throws IOException { + SshAgentVersionResponse msg = new SshAgentVersionResponse(2); + sendMessage(msg); + } + + /** + * Send the agents key list to the remote side. This supplies all the + * public keys. + * + * @throws IOException if an IO error occurs + */ + protected void sendAgentKeyList() throws IOException { + SshAgentKeyList msg = new SshAgentKeyList(keystore.getPublicKeys()); + sendMessage(msg); + } + + /** + * Send the completed signing operation data. + * + * @param data the data generating from the signing operation + * + * @throws IOException if an IO error occurs + */ + protected void sendOperationComplete(byte[] data) throws IOException { + SshAgentOperationComplete msg = new SshAgentOperationComplete(data); + sendMessage(msg); + } + + /** + * Send some random data to the remote side. + * + * @param data some random data + * + * @throws IOException if an IO error occurs + */ + protected void sendRandomData(byte[] data) throws IOException { + SshAgentRandomData msg = new SshAgentRandomData(data); + sendMessage(msg); + } + + /** + * Send the agent alive message. This is sent to test whether the agent is + * still active + * + * @param padding some random padding for the message + * + * @throws IOException if an IO error occurs + */ + protected void sendAgentAlive(byte[] padding) throws IOException { + SshAgentAlive msg = new SshAgentAlive(padding); + sendMessage(msg); + } + + /** + * Sends a subsystem message. + * + * @param msg the subsystem message to send + * + * @throws IOException if an IO error occurs + */ + protected void sendMessage(SubsystemMessage msg) throws IOException { + log.info("Sending message " + msg.getMessageName()); + + byte[] msgdata = msg.toByteArray(); + out.write(ByteArrayWriter.encodeInt(msgdata.length)); + out.write(msgdata); + out.flush(); + } + + /** + * Called when a forwarding notice is recceived from the remote side. + * + * @param msg the forwarding notice + */ + protected void onForwardingNotice(SshAgentForwardingNotice msg) { + forwardingNodes.add(new ForwardingNotice(msg.getRemoteHostname(), + msg.getRemoteIPAddress(), msg.getRemotePort())); + } + + /** + * Called when the remote side requests the version number of this + * protocol. + * + * @param msg the version request message + * + * @throws IOException if an IO error occurs + */ + protected void onRequestVersion(SshAgentRequestVersion msg) + throws IOException { + sendVersionResponse(); + } + + /** + * Called when the remote side adds a key the agent. + * + * @param msg the message containing the key + * + * @throws IOException if an IO error occurs + */ + protected void onAddKey(SshAgentAddKey msg) throws IOException { + if (keystore.addKey(msg.getPrivateKey(), msg.getPublicKey(), + msg.getDescription(), msg.getKeyConstraints())) { + sendAgentSuccess(); + } else { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_FAILURE); + } + } + + /** + * Called when the remote side requests that all keys be removed from the + * agent. + * + * @param msg the delete all keys message + * + * @throws IOException if an IO error occurs + */ + protected void onDeleteAllKeys(SshAgentDeleteAllKeys msg) + throws IOException { + keystore.deleteAllKeys(); + sendAgentSuccess(); + } + + /** + * Called by the remote side when a list of the agents keys is required + * + * @param msg the list all keys message + * + * @throws IOException if an IO error occurs + */ + protected void onListKeys(SshAgentListKeys msg) throws IOException { + sendAgentKeyList(); + } + + /** + * Called by the remote side to initiate a private key operation. + * + * @param msg the private key operation message + * + * @throws IOException if an IO error occurs + */ + protected void onPrivateKeyOp(SshAgentPrivateKeyOp msg) + throws IOException { + try { + if (msg.getOperation().equals("sign")) { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_KEY_NOT_SUITABLE); + } else if (msg.getOperation().equals("hash-and-sign")) { + byte[] sig = keystore.performHashAndSign(msg.getPublicKey(), + forwardingNodes, msg.getOperationData()); + sendOperationComplete(sig); + } else if (msg.getOperation().equals("decrypt")) { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_KEY_NOT_SUITABLE); + } else if (msg.getOperation().equals("ssh1-challenge-response")) { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_KEY_NOT_SUITABLE); + } else { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_UNSUPPORTED_OP); + } + } catch (KeyTimeoutException ex) { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_TIMEOUT); + } catch (InvalidSshKeyException ex) { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_KEY_NOT_FOUND); + } + } + + /** + * Called by the remote side to delete a key from the agent + * + * @param msg the message containin the key to delete + * + * @throws IOException if an IO error occurs + */ + protected void onDeleteKey(SshAgentDeleteKey msg) throws IOException { + if (keystore.deleteKey(msg.getPublicKey(), msg.getDescription())) { + sendAgentSuccess(); + } else { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_KEY_NOT_FOUND); + } + } + + /** + * Called by the remote side when the agent is to be locked + * + * @param msg the message containing a password + * + * @throws IOException if an IO error occurs + */ + protected void onLock(SshAgentLock msg) throws IOException { + if (keystore.lock(msg.getPassword())) { + sendAgentSuccess(); + } else { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_DENIED); + } + } + + /** + * Called by the remote side when the agent is to be unlocked + * + * @param msg the message containin the password + * + * @throws IOException if an IO error occurs + */ + protected void onUnlock(SshAgentUnlock msg) throws IOException { + if (keystore.unlock(msg.getPassword())) { + sendAgentSuccess(); + } else { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_DENIED); + } + } + + /** + * Called when a ping message is received + * + * @param msg the ping message containing some padding + * + * @throws IOException if an IO error occurs + */ + protected void onPing(SshAgentPing msg) throws IOException { + sendAgentAlive(msg.getPadding()); + } + + /** + * Called when the remote side sends a random message + * + * @param msg the random message + * + * @throws IOException if an IO error occurs + */ + protected void onRandom(SshAgentRandom msg) throws IOException { + if (msg.getLength() > 0) { + byte[] random = new byte[msg.getLength()]; + ConfigurationLoader.getRND().nextBytes(random); + sendRandomData(random); + } else { + sendAgentFailure(SshAgentFailure.SSH_AGENT_ERROR_FAILURE); + } + } + + /** + * The connection thread + */ + public void run() { + try { + log.info("Starting agent connection thread"); + + byte[] lendata = new byte[4]; + byte[] msgdata; + int len; + int read; + boolean alive = true; + + while (alive) { + // Read the first 4 bytes to determine the length of the message + len = 0; + + while (len < lendata.length) { + try { + read = 0; + read = in.read(lendata, len, lendata.length - len); + + if (read >= 0) { + len += read; + } else { + alive = false; + + break; + } + } catch (InterruptedIOException ex) { + if (ex.bytesTransferred > 0) { + len += ex.bytesTransferred; + } + } + } + + if (alive) { + len = (int) ByteArrayReader.readInt(lendata, 0); + msgdata = new byte[len]; + len = 0; + + while (len < msgdata.length) { + try { + len += in.read(msgdata, len, msgdata.length - len); + } catch (InterruptedIOException ex1) { + len += ex1.bytesTransferred; + } + } + + onMessageReceived(msgdata); + } + } + } catch (IOException ex) { + log.info("The agent connection terminated"); + } finally { + try { + socket.close(); + } catch (Exception ex) { + } + } + + log.info("Exiting agent connection thread"); + } + + /** + * Process a message and route to the handler method + * + * @param msgdata the raw message received + * + * @throws IOException if an IO error occurs + */ + protected void onMessageReceived(byte[] msgdata) throws IOException { + switch ((int) (msgdata[0] & 0xFF)) { + case SshAgentForwardingNotice.SSH_AGENT_FORWARDING_NOTICE: { + log.info("Agent forwarding notice received"); + + SshAgentForwardingNotice msg = new SshAgentForwardingNotice(); + msg.fromByteArray(msgdata); + onForwardingNotice(msg); + + break; + } + + case SshAgentRequestVersion.SSH_AGENT_REQUEST_VERSION: { + log.info("Agent version request received"); + + SshAgentRequestVersion msg = new SshAgentRequestVersion(); + msg.fromByteArray(msgdata); + onRequestVersion(msg); + + break; + } + + case SshAgentAddKey.SSH_AGENT_ADD_KEY: { + log.info("Adding key to agent"); + + SshAgentAddKey msg = new SshAgentAddKey(); + msg.fromByteArray(msgdata); + onAddKey(msg); + + break; + } + + case SshAgentDeleteAllKeys.SSH_AGENT_DELETE_ALL_KEYS: { + log.info("Deleting all keys from agent"); + + SshAgentDeleteAllKeys msg = new SshAgentDeleteAllKeys(); + msg.fromByteArray(msgdata); + onDeleteAllKeys(msg); + + break; + } + + case SshAgentListKeys.SSH_AGENT_LIST_KEYS: { + log.info("Listing agent keys"); + + SshAgentListKeys msg = new SshAgentListKeys(); + msg.fromByteArray(msgdata); + onListKeys(msg); + + break; + } + + case SshAgentPrivateKeyOp.SSH_AGENT_PRIVATE_KEY_OP: { + log.info("Performing agent private key operation"); + + SshAgentPrivateKeyOp msg = new SshAgentPrivateKeyOp(); + msg.fromByteArray(msgdata); + onPrivateKeyOp(msg); + + break; + } + + case SshAgentDeleteKey.SSH_AGENT_DELETE_KEY: { + log.info("Deleting key from agent"); + + SshAgentDeleteKey msg = new SshAgentDeleteKey(); + msg.fromByteArray(msgdata); + onDeleteKey(msg); + + break; + } + + case SshAgentLock.SSH_AGENT_LOCK: { + log.info("Locking agent"); + + SshAgentLock msg = new SshAgentLock(); + msg.fromByteArray(msgdata); + onLock(msg); + + break; + } + + case SshAgentUnlock.SSH_AGENT_UNLOCK: { + log.info("Unlocking agent"); + + SshAgentUnlock msg = new SshAgentUnlock(); + msg.fromByteArray(msgdata); + onUnlock(msg); + + break; + } + + case SshAgentPing.SSH_AGENT_PING: { + log.info("Ping Ping Ping Ping Ping"); + + SshAgentPing msg = new SshAgentPing(); + msg.fromByteArray(msgdata); + onPing(msg); + + break; + } + + case SshAgentRandom.SSH_AGENT_RANDOM: { + log.info("Random message received"); + + SshAgentRandom msg = new SshAgentRandom(); + msg.fromByteArray(msgdata); + onRandom(msg); + + break; + } + + default: + throw new IOException("Unrecognized message type " + + String.valueOf(msgdata[0]) + " received"); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentDeleteAllKeys.java b/src/com/sshtools/j2ssh/agent/SshAgentDeleteAllKeys.java new file mode 100644 index 0000000000000000000000000000000000000000..74ede1b8aabe647167799d0257ae35e54489d078 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentDeleteAllKeys.java @@ -0,0 +1,80 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +class SshAgentDeleteAllKeys extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_DELETE_ALL_KEYS = 203; + + /** + * Creates a new SshAgentDeleteAllKeys object. + */ + public SshAgentDeleteAllKeys() { + super(SSH_AGENT_DELETE_ALL_KEYS); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_DELETE_ALL_KEYS"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentDeleteKey.java b/src/com/sshtools/j2ssh/agent/SshAgentDeleteKey.java new file mode 100644 index 0000000000000000000000000000000000000000..8fbf8dea16ad93c279537865d665ac85027070ef --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentDeleteKey.java @@ -0,0 +1,131 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.IOException; + + +class SshAgentDeleteKey extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_DELETE_KEY = 207; + SshPublicKey pubkey; + String description; + + /** + * Creates a new SshAgentDeleteKey object. + */ + public SshAgentDeleteKey() { + super(SSH_AGENT_DELETE_KEY); + } + + /** + * Creates a new SshAgentDeleteKey object. + * + * @param pubkey + * @param description + */ + public SshAgentDeleteKey(SshPublicKey pubkey, String description) { + super(SSH_AGENT_DELETE_KEY); + this.pubkey = pubkey; + this.description = description; + } + + /** + * + * + * @return + */ + public SshPublicKey getPublicKey() { + return pubkey; + } + + /** + * + * + * @return + */ + public String getDescription() { + return description; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_DELETE_KEY"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeBinaryString(pubkey.getEncoded()); + baw.writeString(description); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + pubkey = SshKeyPairFactory.decodePublicKey(bar.readBinaryString()); + description = bar.readString(); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentFailure.java b/src/com/sshtools/j2ssh/agent/SshAgentFailure.java new file mode 100644 index 0000000000000000000000000000000000000000..0515cf8613bb79da7e057bb16d57b53b3e7a6443 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentFailure.java @@ -0,0 +1,139 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentFailure extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_FAILURE = 102; + + /** */ + public static final int SSH_AGENT_ERROR_TIMEOUT = 1; + + /** */ + public static final int SSH_AGENT_ERROR_KEY_NOT_FOUND = 2; + + /** */ + public static final int SSH_AGENT_ERROR_DECRYPT_FAILED = 3; + + /** */ + public static final int SSH_AGENT_ERROR_SIZE_ERROR = 4; + + /** */ + public static final int SSH_AGENT_ERROR_KEY_NOT_SUITABLE = 5; + + /** */ + public static final int SSH_AGENT_ERROR_DENIED = 6; + + /** */ + public static final int SSH_AGENT_ERROR_FAILURE = 7; + + /** */ + public static final int SSH_AGENT_ERROR_UNSUPPORTED_OP = 8; + private int errorcode; + + /** + * Creates a new SshAgentFailure object. + */ + public SshAgentFailure() { + super(SSH_AGENT_FAILURE); + } + + /** + * Creates a new SshAgentFailure object. + * + * @param errorcode + */ + public SshAgentFailure(int errorcode) { + super(SSH_AGENT_FAILURE); + this.errorcode = errorcode; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_FAILURE"; + } + + /** + * + * + * @return + */ + public int getErrorCode() { + return errorcode; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeInt(errorcode); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + errorcode = (int) bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentForwardingListener.java b/src/com/sshtools/j2ssh/agent/SshAgentForwardingListener.java new file mode 100644 index 0000000000000000000000000000000000000000..d36755b2daf4bcbfb2fd340213526f2cedd2ea3d --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentForwardingListener.java @@ -0,0 +1,202 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.connection.ConnectionProtocol; +import com.sshtools.j2ssh.util.StartStopState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; + +import java.util.HashMap; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshAgentForwardingListener { + private static Log log = LogFactory.getLog(SshAgentForwardingListener.class); + private static HashMap agents = new HashMap(); + ServerSocket server; + int port; + String location; + StartStopState state = new StartStopState(StartStopState.STOPPED); + Thread thread; + ConnectionProtocol connection; + Vector references = new Vector(); + String sessionId; + + SshAgentForwardingListener(String sessionId, ConnectionProtocol connection) { + log.info("Forwarding agent started"); + this.sessionId = sessionId; + this.connection = connection; + port = selectPort(); + location = "localhost:" + String.valueOf(port); + thread = new Thread(new Runnable() { + public void run() { + state.setValue(StartStopState.STARTED); + + try { + server = new ServerSocket(port, 5, + InetAddress.getByName("localhost")); + + //server.bind(new InetSocketAddress("localhost", port)); + Socket socket; + + while ((state.getValue() == StartStopState.STARTED) && + ((socket = server.accept()) != null)) { + AgentSocketChannel channel = new AgentSocketChannel(true); + channel.bindSocket(socket); + + if (!SshAgentForwardingListener.this.connection.openChannel( + channel)) { + log.warn( + "Failed to open agent forwarding channel"); + } + } + } catch (Exception e) { + if (state.getValue() == StartStopState.STARTED) { + log.warn("Forwarding agent socket failed", e); + } + } + + state.setValue(StartStopState.STOPPED); + } + }); + } + + /** + * + * + * @return + */ + public String getConfiguration() { + return location; + } + + /** + * + * + * @param obj + */ + public void addReference(Object obj) { + if (!references.contains(obj)) { + references.add(obj); + } + } + + /** + * + * + * @param obj + */ + public void removeReference(Object obj) { + if (references.contains(obj)) { + references.remove(obj); + + if (references.size() == 0) { + stop(); + agents.remove(sessionId); + } + } + } + + /** + * + * + * @throws IOException + */ + public void start() throws IOException { + thread.start(); + } + + /** + * + * + * @return + */ + public int getPort() { + return port; + } + + /** + * + */ + public void stop() { + try { + state.setValue(StartStopState.STOPPED); + server.close(); + } catch (IOException ex) { + } + } + + private int selectPort() { + return 49152 + + (int) Math.round(((float) 16383 * ConfigurationLoader.getRND() + .nextFloat())); + } + + /** + * + * + * @param sessionId + * @param connection + * + * @return + * + * @throws AgentNotAvailableException + */ + public static SshAgentForwardingListener getInstance(String sessionId, + ConnectionProtocol connection) throws AgentNotAvailableException { + if (agents.containsKey(sessionId)) { + SshAgentForwardingListener agent = (SshAgentForwardingListener) agents.get(sessionId); + + return agent; + } else { + try { + SshAgentForwardingListener agent = new SshAgentForwardingListener(sessionId, + connection); + agent.start(); + agents.put(sessionId, agent); + + return agent; + } catch (IOException ex) { + throw new AgentNotAvailableException(); + } + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentForwardingNotice.java b/src/com/sshtools/j2ssh/agent/SshAgentForwardingNotice.java new file mode 100644 index 0000000000000000000000000000000000000000..f64ba48aefcd84528e1810b88f623e3bca68c300 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentForwardingNotice.java @@ -0,0 +1,145 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentForwardingNotice extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_FORWARDING_NOTICE = 206; + String remoteHostname; + String remoteIPAddress; + UnsignedInteger32 remotePort; + + /** + * Creates a new SshAgentForwardingNotice object. + */ + public SshAgentForwardingNotice() { + super(SSH_AGENT_FORWARDING_NOTICE); + } + + /** + * Creates a new SshAgentForwardingNotice object. + * + * @param remoteHostname + * @param remoteIPAddress + * @param remotePort + */ + public SshAgentForwardingNotice(String remoteHostname, + String remoteIPAddress, int remotePort) { + super(SSH_AGENT_FORWARDING_NOTICE); + this.remoteHostname = remoteHostname; + this.remoteIPAddress = remoteIPAddress; + this.remotePort = new UnsignedInteger32(remotePort); + } + + /** + * + * + * @return + */ + public String getRemoteHostname() { + return remoteHostname; + } + + /** + * + * + * @return + */ + public String getRemoteIPAddress() { + return remoteIPAddress; + } + + /** + * + * + * @return + */ + public int getRemotePort() { + return remotePort.intValue(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_FORWARDING_NOTICE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeString(remoteHostname); + baw.writeString(remoteIPAddress); + baw.writeUINT32(remotePort); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + remoteHostname = bar.readString(); + remoteIPAddress = bar.readString(); + remotePort = bar.readUINT32(); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentKeyList.java b/src/com/sshtools/j2ssh/agent/SshAgentKeyList.java new file mode 100644 index 0000000000000000000000000000000000000000..c1e167d3b0ef49bb3932c4c259752f03985e0b21 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentKeyList.java @@ -0,0 +1,145 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + + +class SshAgentKeyList extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_KEY_LIST = 104; + private Map keys; + + /** + * Creates a new SshAgentKeyList object. + * + * @param keys + */ + public SshAgentKeyList(Map keys) { + super(SSH_AGENT_KEY_LIST); + this.keys = keys; + } + + /** + * Creates a new SshAgentKeyList object. + */ + public SshAgentKeyList() { + super(SSH_AGENT_KEY_LIST); + this.keys = new HashMap(); + } + + /** + * + * + * @return + */ + public Map getKeys() { + return keys; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_KEY_LIST"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeInt(keys.size()); + + Map.Entry entry; + Iterator it = keys.entrySet().iterator(); + SshPublicKey key; + String description; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + key = (SshPublicKey) entry.getKey(); + description = (String) entry.getValue(); + baw.writeBinaryString(key.getEncoded()); + baw.writeString(description); + } + } catch (IOException ex) { + throw new InvalidMessageException("Failed to write message data"); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + int num = (int) bar.readInt(); + SshPublicKey key; + String description; + byte[] buf; + + for (int i = 0; i < num; i++) { + buf = bar.readBinaryString(); + key = SshKeyPairFactory.decodePublicKey(buf); + description = bar.readString(); + keys.put(key, description); + } + } catch (IOException ex) { + throw new InvalidMessageException("Failed to read message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentListKeys.java b/src/com/sshtools/j2ssh/agent/SshAgentListKeys.java new file mode 100644 index 0000000000000000000000000000000000000000..e8c777196ef82c940fee10b12fcb57911b295558 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentListKeys.java @@ -0,0 +1,80 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +class SshAgentListKeys extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_LIST_KEYS = 204; + + /** + * Creates a new SshAgentListKeys object. + */ + public SshAgentListKeys() { + super(SSH_AGENT_LIST_KEYS); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_LIST_KEYS"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentLock.java b/src/com/sshtools/j2ssh/agent/SshAgentLock.java new file mode 100644 index 0000000000000000000000000000000000000000..08935903eaea19d401acc0a6fc91db4674ce20d7 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentLock.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentLock extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_LOCK = 208; + String password; + + /** + * Creates a new SshAgentLock object. + */ + public SshAgentLock() { + super(SSH_AGENT_LOCK); + } + + /** + * Creates a new SshAgentLock object. + * + * @param password + */ + public SshAgentLock(String password) { + super(SSH_AGENT_LOCK); + this.password = password; + } + + /** + * + * + * @return + */ + public String getPassword() { + return password; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_LOCK"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeString(password); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + password = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentOperationComplete.java b/src/com/sshtools/j2ssh/agent/SshAgentOperationComplete.java new file mode 100644 index 0000000000000000000000000000000000000000..81d576947780b330f16bf6eb27b455e12a60ffa6 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentOperationComplete.java @@ -0,0 +1,110 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + +import java.io.IOException; + + +class SshAgentOperationComplete extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_OPERATION_COMPLETE = 105; + private byte[] data; + + /** + * Creates a new SshAgentOperationComplete object. + */ + public SshAgentOperationComplete() { + super(SSH_AGENT_OPERATION_COMPLETE); + } + + /** + * Creates a new SshAgentOperationComplete object. + * + * @param data + */ + public SshAgentOperationComplete(byte[] data) { + super(SSH_AGENT_OPERATION_COMPLETE); + this.data = data; + } + + /** + * + * + * @return + */ + public byte[] getData() { + return data; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_OPERATION_COMPLETE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeBinaryString(data); + } catch (IOException ioe) { + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + data = bar.readBinaryString(); + } catch (IOException ioe) { + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentPing.java b/src/com/sshtools/j2ssh/agent/SshAgentPing.java new file mode 100644 index 0000000000000000000000000000000000000000..c4d0add0e17392e9d8172452b696b3af5c72101b --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentPing.java @@ -0,0 +1,116 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentPing extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_PING = 212; + private byte[] padding; + + /** + * Creates a new SshAgentPing object. + */ + public SshAgentPing() { + super(SSH_AGENT_PING); + } + + /** + * Creates a new SshAgentPing object. + * + * @param padding + */ + public SshAgentPing(byte[] padding) { + super(SSH_AGENT_PING); + this.padding = padding; + } + + /** + * + * + * @return + */ + public byte[] getPadding() { + return padding; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_PING"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.write(padding); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + padding = new byte[bar.available()]; + bar.read(padding); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentPrivateKeyOp.java b/src/com/sshtools/j2ssh/agent/SshAgentPrivateKeyOp.java new file mode 100644 index 0000000000000000000000000000000000000000..bada4d3209d2a0849f923fe3ea148ffbf9ed5a14 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentPrivateKeyOp.java @@ -0,0 +1,147 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.IOException; + + +class SshAgentPrivateKeyOp extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_PRIVATE_KEY_OP = 205; + SshPublicKey pubkey; + String operation; + byte[] data; + + /** + * Creates a new SshAgentPrivateKeyOp object. + */ + public SshAgentPrivateKeyOp() { + super(SSH_AGENT_PRIVATE_KEY_OP); + } + + /** + * Creates a new SshAgentPrivateKeyOp object. + * + * @param pubkey + * @param operation + * @param data + */ + public SshAgentPrivateKeyOp(SshPublicKey pubkey, String operation, + byte[] data) { + super(SSH_AGENT_PRIVATE_KEY_OP); + this.pubkey = pubkey; + this.operation = operation; + this.data = data; + } + + /** + * + * + * @return + */ + public SshPublicKey getPublicKey() { + return pubkey; + } + + /** + * + * + * @return + */ + public String getOperation() { + return operation; + } + + /** + * + * + * @return + */ + public byte[] getOperationData() { + return data; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_PRIVATE_KEY_OP"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeString(operation); + baw.writeBinaryString(pubkey.getEncoded()); + baw.write(data); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + operation = bar.readString(); + pubkey = SshKeyPairFactory.decodePublicKey(bar.readBinaryString()); + data = new byte[bar.available()]; + bar.read(data); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentRandom.java b/src/com/sshtools/j2ssh/agent/SshAgentRandom.java new file mode 100644 index 0000000000000000000000000000000000000000..4780f52a0cee58a1d3b16685975b1106dd11142e --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentRandom.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentRandom extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_RANDOM = 213; + private int length; + + /** + * Creates a new SshAgentRandom object. + */ + public SshAgentRandom() { + super(SSH_AGENT_RANDOM); + } + + /** + * Creates a new SshAgentRandom object. + * + * @param length + */ + public SshAgentRandom(int length) { + super(SSH_AGENT_RANDOM); + this.length = length; + } + + /** + * + * + * @return + */ + public int getLength() { + return length; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_RANDOM"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeInt(length); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + length = (int) bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentRandomData.java b/src/com/sshtools/j2ssh/agent/SshAgentRandomData.java new file mode 100644 index 0000000000000000000000000000000000000000..3af1976b95f23dbdb7e78b9107313229bc6eb3b6 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentRandomData.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentRandomData extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_RANDOM_DATA = 106; + private byte[] data; + + /** + * Creates a new SshAgentRandomData object. + */ + public SshAgentRandomData() { + super(SSH_AGENT_RANDOM_DATA); + } + + /** + * Creates a new SshAgentRandomData object. + * + * @param data + */ + public SshAgentRandomData(byte[] data) { + super(SSH_AGENT_RANDOM_DATA); + this.data = data; + } + + /** + * + * + * @return + */ + public byte[] getRandomData() { + return data; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_RANDOM_DATA"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeBinaryString(data); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + data = bar.readBinaryString(); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentRequestVersion.java b/src/com/sshtools/j2ssh/agent/SshAgentRequestVersion.java new file mode 100644 index 0000000000000000000000000000000000000000..2aa2e6e1cd5ccfce56c2e263408dd748431d78fd --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentRequestVersion.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentRequestVersion extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_REQUEST_VERSION = 1; + String version; + + /** + * Creates a new SshAgentRequestVersion object. + */ + public SshAgentRequestVersion() { + super(SSH_AGENT_REQUEST_VERSION); + } + + /** + * Creates a new SshAgentRequestVersion object. + * + * @param version + */ + public SshAgentRequestVersion(String version) { + super(SSH_AGENT_REQUEST_VERSION); + this.version = version; + } + + /** + * + * + * @return + */ + public String getVersion() { + return version; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_REQUEST_VERSION"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeString(version); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + version = bar.readString(); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentSocketListener.java b/src/com/sshtools/j2ssh/agent/SshAgentSocketListener.java new file mode 100644 index 0000000000000000000000000000000000000000..eac31e6ca3e07a1a49d9f69718a2962c71be8cd3 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentSocketListener.java @@ -0,0 +1,202 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.util.StartStopState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshAgentSocketListener { + private static Log log = LogFactory.getLog(SshAgentSocketListener.class); + StartStopState state = new StartStopState(StartStopState.STOPPED); + KeyStore keystore; + ServerSocket server; + int port; + Thread thread; + String location; + + /** + * Creates a new SshAgentSocketListener object. + * + * @param location the location of the listening agent. This should be a + * random port on the localhost such as localhost:15342 + * @param keystore the keystore for agent operation + * + * @throws AgentNotAvailableException if the location specifies an invalid + * location + */ + public SshAgentSocketListener(String location, KeyStore keystore) + throws AgentNotAvailableException { + log.info("New SshAgent instance created"); + + // Verify the agent location + this.location = location; + + if (location == null) { + throw new AgentNotAvailableException(); + } + + this.location = location; + + int idx = location.indexOf(":"); + + if (idx == -1) { + throw new AgentNotAvailableException(); + } + + String host = location.substring(0, idx); + port = Integer.parseInt(location.substring(idx + 1)); + this.keystore = keystore; + + try { + server = new ServerSocket(port, 5, InetAddress.getByName(host)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Get the agent listeners state + * + * @return the current state of the listener + */ + public StartStopState getState() { + return state; + } + + /** + * Starts the agent listener thread + */ + public void start() { + thread = new Thread(new Runnable() { + public void run() { + try { + Socket socket; + System.setProperty("sshtools.agent", location); + state.setValue(StartStopState.STARTED); + + while ((socket = server.accept()) != null) { + SshAgentConnection agentClient = new SshAgentConnection(keystore, + socket.getInputStream(), + socket.getOutputStream()); + } + + thread = null; + } catch (IOException ex) { + log.info("The agent listener closed: " + + ex.getMessage()); + } finally { + state.setValue(StartStopState.STOPPED); + } + } + }); + thread.start(); + } + + /** + * The current port of the agent listener + * + * @return the integer port + */ + public int getPort() { + return port; + } + + /** + * Stops the agent listener + */ + public void stop() { + try { + server.close(); + } catch (IOException ex) { + } + } + + /** + * Gets the underlying keystore for this agent listener. + * + * @return the keystore + */ + protected KeyStore getKeystore() { + return keystore; + } + + /** + * Configure a new random port for the agent listener. + * + * @return the random port for this agent. + */ + public static int configureNewLocation() { + return 49152 + (int) Math.round(((float) 16383 * Math.random())); + } + + /** + * The main entry point for the application. This method currently accepts + * the -start parameter which will look for the sshtools.agent system + * property. To configure the agent and to get a valid location call with + * -configure, set the system sshtools.home system property and start. + * + * @param args the programs arguments + */ + public static void main(String[] args) { + if (args.length > 0) { + if (args[0].equals("-start")) { + Thread thread = new Thread(new Runnable() { + public void run() { + try { + SshAgentSocketListener agent = new SshAgentSocketListener(System.getProperty( + "sshtools.agent"), + new KeyStore()); + agent.start(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + thread.start(); + } + + if (args[0].equals("-configure")) { + System.out.println("SET SSHTOOLS_AGENT=localhost:" + + String.valueOf(configureNewLocation())); + } + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentSuccess.java b/src/com/sshtools/j2ssh/agent/SshAgentSuccess.java new file mode 100644 index 0000000000000000000000000000000000000000..3520f035916779db6a458ea9599746cf68f532c8 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentSuccess.java @@ -0,0 +1,80 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +class SshAgentSuccess extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_SUCCESS = 101; + + /** + * Creates a new SshAgentSuccess object. + */ + public SshAgentSuccess() { + super(SSH_AGENT_SUCCESS); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_SUCCESS"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentUnlock.java b/src/com/sshtools/j2ssh/agent/SshAgentUnlock.java new file mode 100644 index 0000000000000000000000000000000000000000..0709741470e03501bd08b331b3291ed81550a213 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentUnlock.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentUnlock extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_UNLOCK = 209; + String password; + + /** + * Creates a new SshAgentUnlock object. + */ + public SshAgentUnlock() { + super(SSH_AGENT_UNLOCK); + } + + /** + * Creates a new SshAgentUnlock object. + * + * @param password + */ + public SshAgentUnlock(String password) { + super(SSH_AGENT_UNLOCK); + this.password = password; + } + + /** + * + * + * @return + */ + public String getPassword() { + return password; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_UNLOCK"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeString(password); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + password = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/agent/SshAgentVersionResponse.java b/src/com/sshtools/j2ssh/agent/SshAgentVersionResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..6effc344e86eb914c99a8c9057d71fbc91c70e38 --- /dev/null +++ b/src/com/sshtools/j2ssh/agent/SshAgentVersionResponse.java @@ -0,0 +1,116 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.agent; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +class SshAgentVersionResponse extends SubsystemMessage { + /** */ + public static final int SSH_AGENT_VERSION_RESPONSE = 103; + UnsignedInteger32 version; + + /** + * Creates a new SshAgentVersionResponse object. + */ + public SshAgentVersionResponse() { + super(SSH_AGENT_VERSION_RESPONSE); + } + + /** + * Creates a new SshAgentVersionResponse object. + * + * @param version + */ + public SshAgentVersionResponse(int version) { + super(SSH_AGENT_VERSION_RESPONSE); + this.version = new UnsignedInteger32(version); + } + + /** + * + * + * @return + */ + public int getVersion() { + return version.intValue(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_AGENT_VERSION_RESPONSE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + baw.writeUINT32(version); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + try { + version = bar.readUINT32(); + } catch (IOException ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolClient.java b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolClient.java new file mode 100644 index 0000000000000000000000000000000000000000..0c59e47c56ef80495e32d0edf6111f07b8787984 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolClient.java @@ -0,0 +1,350 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.SshException; +import com.sshtools.j2ssh.transport.MessageNotAvailableException; +import com.sshtools.j2ssh.transport.MessageStoreEOFException; +import com.sshtools.j2ssh.transport.Service; +import com.sshtools.j2ssh.transport.SshMessage; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.27 $ + */ +public class AuthenticationProtocolClient extends Service { + private static Log log = LogFactory.getLog(AuthenticationProtocolClient.class); + private int[] resultFilter = new int[2]; + private int[] singleIdFilter = new int[3]; + private Vector listeners = new Vector(); + + /** + * Creates a new AuthenticationProtocolClient object. + */ + public AuthenticationProtocolClient() { + super("ssh-userauth"); + resultFilter[0] = SshMsgUserAuthSuccess.SSH_MSG_USERAUTH_SUCCESS; + resultFilter[1] = SshMsgUserAuthFailure.SSH_MSG_USERAUTH_FAILURE; + singleIdFilter[0] = SshMsgUserAuthSuccess.SSH_MSG_USERAUTH_SUCCESS; + singleIdFilter[1] = SshMsgUserAuthFailure.SSH_MSG_USERAUTH_FAILURE; + } + + /** + * + * + * @throws java.io.IOException + */ + protected void onServiceAccept() throws java.io.IOException { + } + + /** + * + */ + protected void onStart() { + } + + /** + * + * + * @param startMode + * + * @throws java.io.IOException + * @throws IOException + */ + protected void onServiceInit(int startMode) throws java.io.IOException { + if (startMode == Service.ACCEPTING_SERVICE) { + throw new IOException( + "The Authentication Protocol client cannot be accepted"); + } + + messageStore.registerMessage(SshMsgUserAuthFailure.SSH_MSG_USERAUTH_FAILURE, + SshMsgUserAuthFailure.class); + messageStore.registerMessage(SshMsgUserAuthSuccess.SSH_MSG_USERAUTH_SUCCESS, + SshMsgUserAuthSuccess.class); + messageStore.registerMessage(SshMsgUserAuthBanner.SSH_MSG_USERAUTH_BANNER, + SshMsgUserAuthBanner.class); + + //messageStore.registerMessage(SshMsgUserAuthPwdChangeReq.SSH_MSG_USERAUTH_PWD_CHANGEREQ, + // SshMsgUserAuthPwdChangeReq.class); + } + + /** + * + * + * @throws java.io.IOException + * @throws IOException + */ + protected void onServiceRequest() throws java.io.IOException { + throw new IOException("This class implements the client protocol only!"); + } + + /** + * + * + * @param listener + */ + public void addEventListener(AuthenticationProtocolListener listener) { + if (listener != null) { + listeners.add(listener); + } + } + + /** + * + * + * @param username + * @param serviceName + * + * @return + * + * @throws IOException + * @throws SshException + */ + public List getAvailableAuths(String username, String serviceName) + throws IOException { + log.info("Requesting authentication methods"); + + SshMessage msg = new SshMsgUserAuthRequest(username, serviceName, + "none", null); + transport.sendMessage(msg, this); + + try { + msg = messageStore.getMessage(resultFilter); + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for an authentication message"); + } + + if (msg instanceof SshMsgUserAuthFailure) { + return ((SshMsgUserAuthFailure) msg).getAvailableAuthentications(); + } else { + throw new IOException( + "None request returned success! Insecure feature not supported"); + } + } + + /** + * + * + * @param auth + * @param serviceToStart + * + * @return + * + * @throws IOException + * @throws SshException + */ + public int authenticate(SshAuthenticationClient auth, Service serviceToStart) + throws IOException { + try { + if (!auth.canAuthenticate() && auth.canPrompt()) { + SshAuthenticationPrompt prompt = auth.getAuthenticationPrompt(); + + if (!prompt.showPrompt(auth)) { + return AuthenticationProtocolState.CANCELLED; + } + } + + auth.authenticate(this, serviceToStart.getServiceName()); + + SshMessage msg = parseMessage(messageStore.getMessage(resultFilter)); + + // We should not get this far + throw new AuthenticationProtocolException( + "Unexpected authentication message " + msg.getMessageName()); + } catch (TerminatedStateException tse) { + if (tse.getState() == AuthenticationProtocolState.COMPLETE) { + serviceToStart.init(Service.ACCEPTING_SERVICE, transport); //, nativeSettings); + serviceToStart.start(); + + for (Iterator it = listeners.iterator(); it.hasNext();) { + AuthenticationProtocolListener listener = (AuthenticationProtocolListener) it.next(); + + if (listener != null) { + listener.onAuthenticationComplete(); + } + } + } + + return tse.getState(); + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for an authentication message"); + } + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + public void sendMessage(SshMessage msg) throws IOException { + transport.sendMessage(msg, this); + } + + /** + * + * + * @return + */ + public byte[] getSessionIdentifier() { + return transport.getSessionIdentifier(); + } + + /** + * + * + * @param cls + * @param messageId + */ + public void registerMessage(Class cls, int messageId) { + messageStore.registerMessage(messageId, cls); + } + + /** + * + * + * @param messageId + * + * @return + * + * @throws TerminatedStateException + * @throws IOException + */ + public SshMessage readMessage(int messageId) + throws TerminatedStateException, IOException { + singleIdFilter[2] = messageId; + + return internalReadMessage(singleIdFilter); + } + + private SshMessage internalReadMessage(int[] messageIdFilter) + throws TerminatedStateException, IOException { + try { + SshMessage msg = messageStore.getMessage(messageIdFilter); + + return parseMessage(msg); + } catch (MessageStoreEOFException meof) { + throw new AuthenticationProtocolException("Failed to read messages"); + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for an authentication message"); + } + } + + /** + * + * + * @param messageId + * + * @return + * + * @throws TerminatedStateException + * @throws IOException + */ + public SshMessage readMessage(int[] messageId) + throws TerminatedStateException, IOException { + int[] messageIdFilter = new int[messageId.length + resultFilter.length]; + System.arraycopy(resultFilter, 0, messageIdFilter, 0, + resultFilter.length); + System.arraycopy(messageId, 0, messageIdFilter, resultFilter.length, + messageId.length); + + return internalReadMessage(messageIdFilter); + } + + /** + * + * + * @throws IOException + * @throws TerminatedStateException + */ + public void readAuthenticationState() + throws IOException, TerminatedStateException { + internalReadMessage(resultFilter); + } + + private SshMessage parseMessage(SshMessage msg) + throws TerminatedStateException { + if (msg instanceof SshMsgUserAuthFailure) { + if (((SshMsgUserAuthFailure) msg).getPartialSuccess()) { + throw new TerminatedStateException(AuthenticationProtocolState.PARTIAL); + } else { + throw new TerminatedStateException(AuthenticationProtocolState.FAILED); + } + } else if (msg instanceof SshMsgUserAuthSuccess) { + throw new TerminatedStateException(AuthenticationProtocolState.COMPLETE); + } else { + return msg; + } + } + + /** + * + * + * @param timeout + * + * @return + * + * @throws IOException + * @throws SshException + */ + public String getBannerMessage(int timeout) throws IOException { + try { + log.debug( + "getBannerMessage is attempting to read the authentication banner"); + + SshMessage msg = messageStore.peekMessage(SshMsgUserAuthBanner.SSH_MSG_USERAUTH_BANNER, + timeout); + + return ((SshMsgUserAuthBanner) msg).getBanner(); + } catch (MessageNotAvailableException e) { + return ""; + } catch (MessageStoreEOFException eof) { + log.error( + "Failed to retreive banner becasue the message store is EOF"); + + return ""; + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for an authentication message"); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolException.java b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolException.java new file mode 100644 index 0000000000000000000000000000000000000000..a8757b6c6c83fb4de8bf6939241be1750eb0ec72 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolException.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class AuthenticationProtocolException extends IOException { + /** + * Creates a new AuthenticationProtocolException object. + * + * @param msg + */ + public AuthenticationProtocolException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolListener.java b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolListener.java new file mode 100644 index 0000000000000000000000000000000000000000..23f9783e89659d6452a9380b558fcc5ceb52fa92 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolListener.java @@ -0,0 +1,54 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + + +/** + * <p> + * Title: + * </p> + * + * <p> + * Description: + * </p> + * + * <p> + * Copyright: Copyright (c) 2003 + * </p> + * + * <p> + * Company: + * </p> + * + * @author Lee David Painter + * @version $Id: AuthenticationProtocolListener.java,v 1.8 2003/09/11 15:35:01 martianx Exp $ + */ +public interface AuthenticationProtocolListener { + /** + * + */ + public void onAuthenticationComplete(); +} diff --git a/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolState.java b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolState.java new file mode 100644 index 0000000000000000000000000000000000000000..d5509bff6993ba27ca8f4d2a2fbffa382fa2ef45 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/AuthenticationProtocolState.java @@ -0,0 +1,50 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class AuthenticationProtocolState { + /** */ + public final static int READY = 1; + + /** */ + public final static int FAILED = 2; + + /** */ + public final static int PARTIAL = 3; + + /** */ + public final static int COMPLETE = 4; + + /** */ + public final static int CANCELLED = 5; +} diff --git a/src/com/sshtools/j2ssh/authentication/HostbasedAuthenticationClient.java b/src/com/sshtools/j2ssh/authentication/HostbasedAuthenticationClient.java new file mode 100644 index 0000000000000000000000000000000000000000..efc133d8f26095bb3b6da23f2a90d6a4bb9e16ed --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/HostbasedAuthenticationClient.java @@ -0,0 +1,276 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.net.InetAddress; + +import java.util.Properties; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class HostbasedAuthenticationClient extends SshAuthenticationClient { + private static Log log = LogFactory.getLog(HostbasedAuthenticationClient.class); + + /** */ + protected SshPrivateKey key; + private String privateKeyFile = null; + private String passphrase = null; + private String clientUser = null; + + /** + * Creates a new HostbasedAuthenticationClient object. + */ + public HostbasedAuthenticationClient() { + } + + /** + * + * + * @param key + */ + public void setKey(SshPrivateKey key) { + this.key = key; + } + + /** + * + */ + public void reset() { + privateKeyFile = null; + passphrase = null; + clientUser = null; + } + + /** + * + * + * @param clientUser + */ + public void setClientUsername(String clientUser) { + this.clientUser = clientUser; + } + + /** + * + * + * @return + */ + public String getMethodName() { + return "hostbased"; + } + + /** + * + * + * @param authentication + * @param serviceToStart + * + * @throws IOException + * @throws TerminatedStateException + * @throws AuthenticationProtocolException + */ + public void authenticate(AuthenticationProtocolClient authentication, + String serviceToStart) throws IOException, TerminatedStateException { + if ((getUsername() == null) || (key == null)) { + throw new AuthenticationProtocolException( + "You must supply a username and a key"); + } + + ByteArrayWriter baw = new ByteArrayWriter(); + log.info("Generating data to sign"); + + SshPublicKey pub = key.getPublicKey(); + InetAddress addr = InetAddress.getLocalHost(); + String hostname = addr.getHostName(); + log.info("Preparing hostbased authentication request for " + hostname); + + // Now prepare and send the message + baw.writeString(pub.getAlgorithmName()); + baw.writeBinaryString(pub.getEncoded()); + baw.writeString(hostname); + + if (clientUser != null) { + baw.writeString(clientUser); + } else { + baw.writeString(getUsername()); + } + + // Create the signature data + ByteArrayWriter data = new ByteArrayWriter(); + data.writeBinaryString(authentication.getSessionIdentifier()); + data.write(SshMsgUserAuthRequest.SSH_MSG_USERAUTH_REQUEST); + data.writeString(getUsername()); + data.writeString(serviceToStart); + data.writeString(getMethodName()); + data.writeString(pub.getAlgorithmName()); + data.writeBinaryString(pub.getEncoded()); + data.writeString(hostname); + + if (clientUser != null) { + data.writeString(clientUser); + } else { + data.writeString(getUsername()); + } + + // Generate the signature + baw.writeBinaryString(key.generateSignature(data.toByteArray())); + + SshMsgUserAuthRequest msg = new SshMsgUserAuthRequest(getUsername(), + serviceToStart, getMethodName(), baw.toByteArray()); + authentication.sendMessage(msg); + } + + /* public boolean showAuthenticationDialog(Component parent) { + SshPrivateKeyFile pkf = null; + if (privateKeyFile == null) { + JFileChooser chooser = new JFileChooser(); + chooser.setFileHidingEnabled(false); + chooser.setDialogTitle("Select Private Key File For Authentication"); + if (chooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + privateKeyFile = chooser.getSelectedFile().getAbsolutePath(); + } + else { + return false; + } + } + FileInputStream in = null; + try { + pkf = SshPrivateKeyFile.parse(new File(privateKeyFile)); + } + catch (InvalidSshKeyException iske) { + JOptionPane.showMessageDialog(parent, iske.getMessage()); + return false; + } + catch (IOException ioe) { + JOptionPane.showMessageDialog(parent, ioe.getMessage()); + } + // Now see if its passphrase protected + if (pkf.isPassphraseProtected()) { + if (passphrase == null) { + // Show the passphrase dialog + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + PassphraseDialog dialog = null; + if (w instanceof Frame) { + dialog = new PassphraseDialog( (Frame) w); + } + else if (w instanceof Dialog) { + dialog = new PassphraseDialog( (Dialog) w); + } + else { + dialog = new PassphraseDialog(); + } + do { + dialog.setVisible(true); + if (dialog.isCancelled()) { + return false; + } + passphrase = new String(dialog.getPassphrase()); + try { + key = pkf.toPrivateKey(passphrase); + break; + } + catch (InvalidSshKeyException ihke) { + dialog.setMessage("Passphrase Invalid! Try again"); + dialog.setMessageForeground(Color.red); + } + } + while (true); + } + else { + try { + key = pkf.toPrivateKey(passphrase); + } + catch (InvalidSshKeyException ihke) { + return false; + } + } + } + else { + try { + key = pkf.toPrivateKey(null); + } + catch (InvalidSshKeyException ihke) { + JOptionPane.showMessageDialog(parent, ihke.getMessage()); + return false; + } + } + return true; + }*/ + public Properties getPersistableProperties() { + Properties properties = new Properties(); + + if (getUsername() != null) { + properties.setProperty("Username", getUsername()); + } + + if (privateKeyFile != null) { + properties.setProperty("PrivateKey", privateKeyFile); + } + + return properties; + } + + /** + * + * + * @param properties + */ + public void setPersistableProperties(Properties properties) { + setUsername(properties.getProperty("Username")); + + if (properties.getProperty("PrivateKey") != null) { + privateKeyFile = properties.getProperty("PrivateKey"); + } + + if (properties.getProperty("Passphrase") != null) { + passphrase = properties.getProperty("Passphrase"); + } + } + + /** + * + * + * @return + */ + public boolean canAuthenticate() { + return ((getUsername() != null) && (key != null)); + } +} diff --git a/src/com/sshtools/j2ssh/authentication/KBIAuthenticationClient.java b/src/com/sshtools/j2ssh/authentication/KBIAuthenticationClient.java new file mode 100644 index 0000000000000000000000000000000000000000..15b4ae191598738a818cf03a9370ce86c2fa5ad0 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/KBIAuthenticationClient.java @@ -0,0 +1,166 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.util.Properties; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class KBIAuthenticationClient extends SshAuthenticationClient { + KBIRequestHandler handler; + + /** + * + * + * @return + */ + public Properties getPersistableProperties() { + return new Properties(); + } + + /** + * + * + * @param handler + */ + public void setKBIRequestHandler(KBIRequestHandler handler) { + this.handler = handler; + } + + /** + * + */ + public void reset() { + } + + /** + * + * + * @param authentication + * @param serviceToStart + * + * @throws com.sshtools.j2ssh.authentication.TerminatedStateException + * + * @throws java.io.IOException + * @throws AuthenticationProtocolException + */ + public void authenticate(AuthenticationProtocolClient authentication, + String serviceToStart) + throws com.sshtools.j2ssh.authentication.TerminatedStateException, + java.io.IOException { + if (handler == null) { + throw new AuthenticationProtocolException( + "A request handler must be set!"); + } + + authentication.registerMessage(SshMsgUserAuthInfoRequest.class, + SshMsgUserAuthInfoRequest.SSH_MSG_USERAUTH_INFO_REQUEST); + + // Send the authentication request + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(""); + baw.writeString(""); + + SshMessage msg = new SshMsgUserAuthRequest(getUsername(), + serviceToStart, getMethodName(), baw.toByteArray()); + authentication.sendMessage(msg); + + // Read a message + while (true) { + msg = authentication.readMessage(SshMsgUserAuthInfoRequest.SSH_MSG_USERAUTH_INFO_REQUEST); + + if (msg instanceof SshMsgUserAuthInfoRequest) { + SshMsgUserAuthInfoRequest request = (SshMsgUserAuthInfoRequest) msg; + KBIPrompt[] prompts = request.getPrompts(); + handler.showPrompts(request.getName(), + request.getInstruction(), prompts); + + // Now send the response message + msg = new SshMsgUserAuthInfoResponse(prompts); + authentication.sendMessage(msg); + } else { + throw new AuthenticationProtocolException( + "Unexpected authentication message " + + msg.getMessageName()); + } + } + } + + /** + * + * + * @return + */ + public boolean canAuthenticate() { + return true; + } + + /** + * + * + * @return + */ + public String getMethodName() { + return "keyboard-interactive"; + } + + /** + * + * + * @param properties + */ + public void setPersistableProperties(Properties properties) { + } + + /* public boolean showAuthenticationDialog(Component parent) + throws java.io.IOException { + final Component myparent = parent; + this.handler = new KBIRequestHandlerDialog(); + // this.handler = new KBIRequestHandler() { + // public void showPrompts(String name, String instructions, + // KBIPrompt[] prompts) { + // if (prompts != null) { + // for (int i = 0; i < prompts.length; i++) { + // // We can echo the response back to the client + // prompts[i].setResponse((JOptionPane + // .showInputDialog(myparent, + // prompts[i].getPrompt(), name, + // JOptionPane.QUESTION_MESSAGE))); + // } + // } + // } + // }; + return true; + }*/ +} diff --git a/src/com/sshtools/j2ssh/authentication/KBIPrompt.java b/src/com/sshtools/j2ssh/authentication/KBIPrompt.java new file mode 100644 index 0000000000000000000000000000000000000000..68c976f60c78d439f2fe1e95ca6b42dabb199468 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/KBIPrompt.java @@ -0,0 +1,86 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class KBIPrompt { + private String prompt; + private String response; + private boolean echo; + + /** + * Creates a new KBIPrompt object. + * + * @param prompt + * @param echo + */ + protected KBIPrompt(String prompt, boolean echo) { + this.prompt = prompt; + this.echo = echo; + } + + /** + * + * + * @return + */ + public String getPrompt() { + return prompt; + } + + /** + * + * + * @return + */ + public boolean echo() { + return echo; + } + + /** + * + * + * @param response + */ + public void setResponse(String response) { + this.response = response; + } + + /** + * + * + * @return + */ + public String getResponse() { + return response; + } +} diff --git a/src/com/sshtools/j2ssh/authentication/KBIRequestHandler.java b/src/com/sshtools/j2ssh/authentication/KBIRequestHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..6780f722b55ca156babe0d30f6178293748472cc --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/KBIRequestHandler.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public interface KBIRequestHandler { + /** + * + * + * @param name + * @param instruction + * @param prompts + */ + public void showPrompts(String name, String instruction, KBIPrompt[] prompts); +} diff --git a/src/com/sshtools/j2ssh/authentication/PasswordAuthenticationClient.java b/src/com/sshtools/j2ssh/authentication/PasswordAuthenticationClient.java new file mode 100644 index 0000000000000000000000000000000000000000..875294669987ed6391c17b4a21da16c7b798202e --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/PasswordAuthenticationClient.java @@ -0,0 +1,188 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.Properties; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class PasswordAuthenticationClient extends SshAuthenticationClient { + private static Log log = LogFactory.getLog(PasswordAuthenticationClient.class); + private PasswordChangePrompt changePrompt = null; + + /** */ + protected String password = null; + + /** + * + * + * @return + */ + public final String getMethodName() { + return "password"; + } + + /** + * + * + * @param password + */ + public final void setPassword(String password) { + this.password = password; + } + + /** + * + */ + public void reset() { + password = null; + } + + /** + * + * + * @param changePrompt + */ + public void setPasswordChangePrompt(PasswordChangePrompt changePrompt) { + this.changePrompt = changePrompt; + } + + /*public boolean showAuthenticationDialog(Component parent) + throws AuthenticationProtocolException { + if (password != null) { + return true; + } + // Create the password authentication dialog + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + PasswordAuthenticationDialog dialog = null; + if (w instanceof Frame) { + dialog = new PasswordAuthenticationDialog((Frame) w); + } else if (w instanceof Dialog) { + dialog = new PasswordAuthenticationDialog((Dialog) w); + } else { + dialog = new PasswordAuthenticationDialog(); + } + // Show the dialog + if (dialog.showPromptForPassword(getUsername())) { + setUsername(dialog.getUsername()); + setPassword(dialog.getPassword()); + return true; + } + return false; + }*/ + /*public void setAuthenticatedTokens(Map tokens) { + }*/ + public void authenticate(AuthenticationProtocolClient authentication, + String serviceToStart) throws IOException, TerminatedStateException { + if ((getUsername() == null) || (password == null)) { + throw new AuthenticationProtocolException( + "Username and password cannot be null!"); + } + + authentication.registerMessage(SshMsgUserAuthPwdChangeReq.class, + SshMsgUserAuthPwdChangeReq.SSH_MSG_USERAUTH_PWD_CHANGEREQ); + + // Send a password authentication request + ByteArrayWriter baw = new ByteArrayWriter(); + baw.write(0); + baw.writeString(password); + + SshMsgUserAuthRequest msg = new SshMsgUserAuthRequest(getUsername(), + serviceToStart, "password", baw.toByteArray()); + authentication.sendMessage(msg); + + SshMsgUserAuthPwdChangeReq pwd = (SshMsgUserAuthPwdChangeReq) authentication.readMessage(SshMsgUserAuthPwdChangeReq.SSH_MSG_USERAUTH_PWD_CHANGEREQ); + + if (changePrompt != null) { + String newpassword = changePrompt.changePassword(pwd.getPrompt()); + + if (newpassword != null) { + log.debug("Setting new password"); + baw = new ByteArrayWriter(); + baw.write(1); + baw.writeString(password); + baw.writeString(newpassword); + msg = new SshMsgUserAuthRequest(getUsername(), serviceToStart, + "password", baw.toByteArray()); + authentication.sendMessage(msg); + } else { + throw new TerminatedStateException(AuthenticationProtocolState.FAILED); + } + } else { + throw new TerminatedStateException(AuthenticationProtocolState.FAILED); + } + } + + /** + * + * + * @return + */ + public Properties getPersistableProperties() { + Properties properties = new Properties(); + + if (getUsername() != null) { + properties.setProperty("Username", getUsername()); + } + + return properties; + } + + /** + * + * + * @param properties + */ + public void setPersistableProperties(Properties properties) { + setUsername(properties.getProperty("Username")); + + if (properties.getProperty("Password") != null) { + setPassword(properties.getProperty("Password")); + } + } + + /** + * + * + * @return + */ + public boolean canAuthenticate() { + return ((getUsername() != null) && (password != null)); + } +} diff --git a/src/com/sshtools/j2ssh/authentication/PasswordChangePrompt.java b/src/com/sshtools/j2ssh/authentication/PasswordChangePrompt.java new file mode 100644 index 0000000000000000000000000000000000000000..5fd9eb47242b08471a39e54ec75b7c678e9e6d94 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/PasswordChangePrompt.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public interface PasswordChangePrompt { + /** + * + * + * @param prompt + * + * @return + */ + public String changePassword(String prompt); +} diff --git a/src/com/sshtools/j2ssh/authentication/PublicKeyAuthenticationClient.java b/src/com/sshtools/j2ssh/authentication/PublicKeyAuthenticationClient.java new file mode 100644 index 0000000000000000000000000000000000000000..11712d0ff08d9982b468b71da295911d02f717a8 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/PublicKeyAuthenticationClient.java @@ -0,0 +1,292 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.SshMessage; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.Properties; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class PublicKeyAuthenticationClient extends SshAuthenticationClient { + private static Log log = LogFactory.getLog(PublicKeyAuthenticationClient.class); + + /** */ + protected SshPrivateKey key; + private String privateKeyFile = null; + private String passphrase = null; + + /** + * Creates a new PublicKeyAuthenticationClient object. + */ + public PublicKeyAuthenticationClient() { + } + + /** + * + * + * @param key + */ + public void setKey(SshPrivateKey key) { + this.key = key; + } + + public void setKeyfile(String privateKeyFile) { + this.privateKeyFile = privateKeyFile; + } + + public String getKeyfile() { + return privateKeyFile; + } + + /** + * + */ + public void reset() { + privateKeyFile = null; + passphrase = null; + } + + /** + * + * + * @return + */ + public String getMethodName() { + return "publickey"; + } + + /** + * + * + * @param authentication + * @param username + * @param serviceToStart + * @param key + * + * @return + * + * @throws IOException + */ + public boolean acceptsKey(AuthenticationProtocolClient authentication, + String username, String serviceToStart, SshPublicKey key) + throws IOException { + authentication.registerMessage(SshMsgUserAuthPKOK.class, + SshMsgUserAuthPKOK.SSH_MSG_USERAUTH_PK_OK); + log.info( + "Determining if server can accept public key for authentication"); + + ByteArrayWriter baw = new ByteArrayWriter(); + + // Now prepare and send the message + baw.write(0); + baw.writeString(key.getAlgorithmName()); + baw.writeBinaryString(key.getEncoded()); + + SshMessage msg = new SshMsgUserAuthRequest(username, serviceToStart, + getMethodName(), baw.toByteArray()); + authentication.sendMessage(msg); + + try { + msg = authentication.readMessage(SshMsgUserAuthPKOK.SSH_MSG_USERAUTH_PK_OK); + + if (msg instanceof SshMsgUserAuthPKOK) { + return true; + } else { + throw new IOException( + "Unexpected message returned from readMessage"); + } + } catch (TerminatedStateException ex) { + return false; + } + } + + /** + * + * + * @param authentication + * @param serviceToStart + * + * @throws IOException + * @throws TerminatedStateException + * @throws AuthenticationProtocolException + */ + public void authenticate(AuthenticationProtocolClient authentication, + String serviceToStart) throws IOException, TerminatedStateException { + if ((getUsername() == null) || (key == null)) { + throw new AuthenticationProtocolException( + "You must supply a username and a key"); + } + + ByteArrayWriter baw = new ByteArrayWriter(); + log.info("Generating data to sign"); + + SshPublicKey pub = key.getPublicKey(); + log.info("Preparing public key authentication request"); + + // Now prepare and send the message + baw.write(1); + baw.writeString(pub.getAlgorithmName()); + baw.writeBinaryString(pub.getEncoded()); + + // Create the signature data + ByteArrayWriter data = new ByteArrayWriter(); + data.writeBinaryString(authentication.getSessionIdentifier()); + data.write(SshMsgUserAuthRequest.SSH_MSG_USERAUTH_REQUEST); + data.writeString(getUsername()); + data.writeString(serviceToStart); + data.writeString(getMethodName()); + data.write(1); + data.writeString(pub.getAlgorithmName()); + data.writeBinaryString(pub.getEncoded()); + + // Generate the signature + baw.writeBinaryString(key.generateSignature(data.toByteArray())); + + SshMsgUserAuthRequest msg = new SshMsgUserAuthRequest(getUsername(), + serviceToStart, getMethodName(), baw.toByteArray()); + authentication.sendMessage(msg); + } + + /*public boolean showAuthenticationDialog(Component parent) { + SshPrivateKeyFile pkf = null; + if (privateKeyFile == null) { + JFileChooser chooser = new JFileChooser(); + chooser.setFileHidingEnabled(false); + chooser.setDialogTitle("Select Private Key File For Authentication"); + if (chooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + privateKeyFile = chooser.getSelectedFile().getAbsolutePath(); + } else { + return false; + } + } + FileInputStream in = null; + try { + pkf = SshPrivateKeyFile.parse(new File(privateKeyFile)); + } catch (InvalidSshKeyException iske) { + JOptionPane.showMessageDialog(parent, iske.getMessage()); + return false; + } catch (IOException ioe) { + JOptionPane.showMessageDialog(parent, ioe.getMessage()); + } + // Now see if its passphrase protected + if (pkf.isPassphraseProtected()) { + if (passphrase == null) { + // Show the passphrase dialog + Window w = (Window) SwingUtilities.getAncestorOfClass(Window.class, + parent); + PassphraseDialog dialog = null; + if (w instanceof Frame) { + dialog = new PassphraseDialog((Frame) w); + } else if (w instanceof Dialog) { + dialog = new PassphraseDialog((Dialog) w); + } else { + dialog = new PassphraseDialog(); + } + do { + dialog.setVisible(true); + if (dialog.isCancelled()) { + return false; + } + passphrase = new String(dialog.getPassphrase()); + try { + key = pkf.toPrivateKey(passphrase); + break; + } catch (InvalidSshKeyException ihke) { + dialog.setMessage("Passphrase Invalid! Try again"); + dialog.setMessageForeground(Color.red); + } + } while (true); + } else { + try { + key = pkf.toPrivateKey(passphrase); + } catch (InvalidSshKeyException ihke) { + return false; + } + } + } else { + try { + key = pkf.toPrivateKey(null); + } catch (InvalidSshKeyException ihke) { + JOptionPane.showMessageDialog(parent, ihke.getMessage()); + return false; + } + } + return true; + }*/ + public Properties getPersistableProperties() { + Properties properties = new Properties(); + + if (getUsername() != null) { + properties.setProperty("Username", getUsername()); + } + + if (privateKeyFile != null) { + properties.setProperty("PrivateKey", privateKeyFile); + } + + return properties; + } + + /** + * + * + * @param properties + */ + public void setPersistableProperties(Properties properties) { + setUsername(properties.getProperty("Username")); + + if (properties.getProperty("PrivateKey") != null) { + privateKeyFile = properties.getProperty("PrivateKey"); + } + + if (properties.getProperty("Passphrase") != null) { + passphrase = properties.getProperty("Passphrase"); + } + } + + /** + * + * + * @return + */ + public boolean canAuthenticate() { + return ((getUsername() != null) && (key != null)); + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshAuthenticationClient.java b/src/com/sshtools/j2ssh/authentication/SshAuthenticationClient.java new file mode 100644 index 0000000000000000000000000000000000000000..1fd8c6e1afed2444388cf6f653589b345700bcba --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshAuthenticationClient.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import java.io.IOException; + +import java.util.Properties; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public abstract class SshAuthenticationClient { + private String username; + private SshAuthenticationPrompt prompt; + + /** + * + * + * @return + */ + public abstract String getMethodName(); + + /** + * + * + * @param authentication + * @param serviceToStart + * + * @throws IOException + * @throws TerminatedStateException + */ + public abstract void authenticate( + AuthenticationProtocolClient authentication, String serviceToStart) + throws IOException, TerminatedStateException; + + /** + * + * + * @param prompt + * + * @throws AuthenticationProtocolException + */ + public void setAuthenticationPrompt(SshAuthenticationPrompt prompt) + throws AuthenticationProtocolException { + //prompt.setInstance(this); + this.prompt = prompt; + } + + /** + * + * + * @return + */ + public SshAuthenticationPrompt getAuthenticationPrompt() { + return prompt; + } + + /** + * + * + * @param username + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * + * + * @return + */ + public String getUsername() { + return username; + } + + /** + * + * + * @return + */ + public abstract Properties getPersistableProperties(); + + /** + * + */ + public abstract void reset(); + + /** + * + * + * @param properties + */ + public abstract void setPersistableProperties(Properties properties); + + /** + * + * + * @return + */ + public abstract boolean canAuthenticate(); + + /** + * + * + * @return + */ + public boolean canPrompt() { + return prompt != null; + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshAuthenticationClientFactory.java b/src/com/sshtools/j2ssh/authentication/SshAuthenticationClientFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d00c762f86dcc22c044e52f1b1200d26e3201ff5 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshAuthenticationClientFactory.java @@ -0,0 +1,175 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.ExtensionAlgorithm; +import com.sshtools.j2ssh.configuration.SshAPIConfiguration; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.FilePermission; + +import java.security.AccessControlException; +import java.security.AccessController; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshAuthenticationClientFactory { + private static Map auths; + private static Log log = LogFactory.getLog(SshAuthenticationClientFactory.class); + + /** */ + public final static String AUTH_PASSWORD = "password"; + + /** */ + public final static String AUTH_PK = "publickey"; + + /** */ + public final static String AUTH_KBI = "keyboard-interactive"; + + /** */ + public final static String AUTH_HOSTBASED = "hostbased"; + + static { + auths = new HashMap(); + log.info("Loading supported authentication methods"); + auths.put(AUTH_PASSWORD, PasswordAuthenticationClient.class); + + // Only allow key authentication if we are able to open local files + try { + if (System.getSecurityManager() != null) { + AccessController.checkPermission(new FilePermission( + "<<ALL FILES>>", "read")); + } + + auths.put(AUTH_PK, PublicKeyAuthenticationClient.class); + } catch (AccessControlException ace) { + log.info( + "The security manager prevents use of Public Key Authentication on the client"); + } + + auths.put(AUTH_KBI, KBIAuthenticationClient.class); + + //auths.put(AUTH_HOSTBASED, HostbasedAuthenticationClient.class); + try { + // Load external methods from configuration file + if (ConfigurationLoader.isConfigurationAvailable( + SshAPIConfiguration.class)) { + SshAPIConfiguration config = (SshAPIConfiguration) ConfigurationLoader.getConfiguration(SshAPIConfiguration.class); + List addons = config.getAuthenticationExtensions(); + Iterator it = addons.iterator(); + + // Add the methods to our supported list + while (it.hasNext()) { + ExtensionAlgorithm method = (ExtensionAlgorithm) it.next(); + String name = method.getAlgorithmName(); + + if (auths.containsKey(name)) { + log.debug("Standard authentication implementation for " + + name + " is being overidden by " + + method.getImplementationClass()); + } else { + log.debug(name + " authentication is implemented by " + + method.getImplementationClass()); + } + + try { + Class cls = ConfigurationLoader.getExtensionClass(method.getImplementationClass()); + Object obj = cls.newInstance(); + + if (obj instanceof SshAuthenticationClient) { + auths.put(name, cls); + } + } catch (Exception e) { + log.warn( + "Failed to load extension authentication implementation" + + method.getImplementationClass(), e); + } + } + } + } catch (ConfigurationException ex) { + } + } + + /** + * Creates a new SshAuthenticationClientFactory object. + */ + protected SshAuthenticationClientFactory() { + } + + /** + * + */ + public static void initialize() { + } + + /** + * + * + * @return + */ + public static List getSupportedMethods() { + // Get the list of ciphers + ArrayList list = new ArrayList(auths.keySet()); + + // Return the list + return list; + } + + /** + * + * + * @param methodName + * + * @return + * + * @throws AlgorithmNotSupportedException + */ + public static SshAuthenticationClient newInstance(String methodName) + throws AlgorithmNotSupportedException { + try { + return (SshAuthenticationClient) ((Class) auths.get(methodName)).newInstance(); + } catch (Exception e) { + throw new AlgorithmNotSupportedException(methodName + + " is not supported!"); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshAuthenticationPrompt.java b/src/com/sshtools/j2ssh/authentication/SshAuthenticationPrompt.java new file mode 100644 index 0000000000000000000000000000000000000000..e51e76db3b26868e01c7e69a0d7790b82b0ab865 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshAuthenticationPrompt.java @@ -0,0 +1,54 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface SshAuthenticationPrompt { + /** + * + * + * @return + */ + public boolean showPrompt(SshAuthenticationClient instance) + throws AuthenticationProtocolException; + + /** + * + * + * @param instance + * + * @throws AuthenticationProtocolException + */ + + /* public void setInstance(SshAuthenticationClient instance) + throws AuthenticationProtocolException;*/ +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthBanner.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthBanner.java new file mode 100644 index 0000000000000000000000000000000000000000..f9f65f2746449f6435750cb336d408381befb764 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthBanner.java @@ -0,0 +1,126 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.25 $ + */ +public class SshMsgUserAuthBanner extends SshMessage { + /** */ + public final static int SSH_MSG_USERAUTH_BANNER = 53; + private String banner; + private String languageTag; + + /** + * Creates a new SshMsgUserAuthBanner object. + */ + public SshMsgUserAuthBanner() { + super(SSH_MSG_USERAUTH_BANNER); + } + + /** + * Creates a new SshMsgUserAuthBanner object. + * + * @param banner + */ + public SshMsgUserAuthBanner(String banner) { + super(SSH_MSG_USERAUTH_BANNER); + this.banner = banner; + this.languageTag = ""; + } + + /** + * + * + * @return + */ + public String getBanner() { + return banner; + } + + /** + * + * + * @return + */ + public String getLanguageTag() { + return languageTag; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_BANNER"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(banner); + baw.writeString(languageTag); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing the message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + banner = bar.readString(); + languageTag = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading the message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthFailure.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthFailure.java new file mode 100644 index 0000000000000000000000000000000000000000..47bd751497b504cfc72aa40ea4d19a576859987d --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthFailure.java @@ -0,0 +1,153 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.23 $ + */ +public class SshMsgUserAuthFailure extends SshMessage { + /** */ + protected final static int SSH_MSG_USERAUTH_FAILURE = 51; + private List auths; + private boolean partialSuccess; + + /** + * Creates a new SshMsgUserAuthFailure object. + */ + public SshMsgUserAuthFailure() { + super(SSH_MSG_USERAUTH_FAILURE); + } + + /** + * Creates a new SshMsgUserAuthFailure object. + * + * @param auths + * @param partialSuccess + * + * @throws InvalidMessageException + */ + public SshMsgUserAuthFailure(String auths, boolean partialSuccess) + throws InvalidMessageException { + super(SSH_MSG_USERAUTH_FAILURE); + loadListFromDelimString(auths); + this.partialSuccess = partialSuccess; + } + + /** + * + * + * @return + */ + public List getAvailableAuthentications() { + return auths; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_FAILURE"; + } + + /** + * + * + * @return + */ + public boolean getPartialSuccess() { + return partialSuccess; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + String authMethods = null; + Iterator it = auths.iterator(); + + while (it.hasNext()) { + authMethods = ((authMethods == null) ? "" : (authMethods + ",")) + + (String) it.next(); + } + + baw.writeString(authMethods); + baw.write((partialSuccess ? 1 : 0)); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + String auths = bar.readString(); + partialSuccess = ((bar.read() != 0) ? true : false); + loadListFromDelimString(auths); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + private void loadListFromDelimString(String list) { + StringTokenizer tok = new StringTokenizer(list, ","); + auths = new ArrayList(); + + while (tok.hasMoreElements()) { + auths.add(tok.nextElement()); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthInfoRequest.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthInfoRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..dccde08a6f821cdaaf9f177bff32e77b702e3dc4 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthInfoRequest.java @@ -0,0 +1,209 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshMsgUserAuthInfoRequest extends SshMessage { + /** */ + public static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60; + private String name; + private String instruction; + private String langtag; + private KBIPrompt[] prompts; + + /** + * Creates a new SshMsgUserAuthInfoRequest object. + */ + public SshMsgUserAuthInfoRequest() { + super(SSH_MSG_USERAUTH_INFO_REQUEST); + } + + /** + * Creates a new SshMsgUserAuthInfoRequest object. + * + * @param name + * @param instruction + * @param langtag + */ + public SshMsgUserAuthInfoRequest(String name, String instruction, + String langtag) { + super(SSH_MSG_USERAUTH_INFO_REQUEST); + this.name = name; + this.instruction = instruction; + this.langtag = langtag; + } + + /** + * + * + * @param prompt + * @param echo + */ + public void addPrompt(String prompt, boolean echo) { + if (prompts == null) { + prompts = new KBIPrompt[1]; + prompts[0] = new KBIPrompt(prompt, echo); + } else { + KBIPrompt[] temp = new KBIPrompt[prompts.length + 1]; + System.arraycopy(prompts, 0, temp, 0, prompts.length); + prompts = temp; + prompts[prompts.length - 1] = new KBIPrompt(prompt, echo); + } + } + + /** + * + * + * @return + */ + public KBIPrompt[] getPrompts() { + return prompts; + } + + /** + * + * + * @return + */ + public String getName() { + return name; + } + + /** + * + * + * @return + */ + public String getInstruction() { + return instruction; + } + + /** + * + * + * @return + */ + public String getLanguageTag() { + return langtag; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_INFO_REQUEST"; + } + + /** + * + * + * @param baw + * + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws com.sshtools.j2ssh.transport.InvalidMessageException { + try { + if (name != null) { + baw.writeString(name); + } else { + baw.writeString(""); + } + + if (instruction != null) { + baw.writeString(instruction); + } else { + baw.writeString(""); + } + + if (langtag != null) { + baw.writeString(langtag); + } else { + baw.writeString(""); + } + + if (prompts == null) { + baw.writeInt(0); + } else { + baw.writeInt(prompts.length); + + for (int i = 0; i < prompts.length; i++) { + baw.writeString(prompts[i].getPrompt()); + baw.write(prompts[i].echo() ? 1 : 0); + } + } + } catch (IOException ioe) { + throw new InvalidMessageException("Failed to write message data"); + } + } + + /** + * + * + * @param bar + * + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws com.sshtools.j2ssh.transport.InvalidMessageException { + try { + name = bar.readString(); + instruction = bar.readString(); + langtag = bar.readString(); + + long num = bar.readInt(); + String prompt; + boolean echo; + + for (int i = 0; i < num; i++) { + prompt = bar.readString(); + echo = (bar.read() == 1); + addPrompt(prompt, echo); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Failed to read message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthInfoResponse.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthInfoResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..46167411c8427fd3ec757775f61ffa7f864e5089 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthInfoResponse.java @@ -0,0 +1,141 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshMsgUserAuthInfoResponse extends SshMessage { + /** */ + public static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61; + private KBIPrompt[] prompts; + String[] responses; + + /** + * Creates a new SshMsgUserAuthInfoResponse object. + */ + public SshMsgUserAuthInfoResponse() { + super(SSH_MSG_USERAUTH_INFO_RESPONSE); + } + + /** + * Creates a new SshMsgUserAuthInfoResponse object. + * + * @param prompts + */ + public SshMsgUserAuthInfoResponse(KBIPrompt[] prompts) { + super(SSH_MSG_USERAUTH_INFO_RESPONSE); + + if (prompts != null) { + responses = new String[prompts.length]; + + for (int i = 0; i < responses.length; i++) { + responses[i] = prompts[i].getResponse(); + } + } + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_INFO_RESPONSE"; + } + + /** + * + * + * @return + */ + public String[] getResponses() { + return responses; + } + + /** + * + * + * @param baw + * + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws com.sshtools.j2ssh.transport.InvalidMessageException { + try { + if (responses == null) { + baw.writeInt(0); + } else { + baw.writeInt(responses.length); + + for (int i = 0; i < responses.length; i++) { + baw.writeString(responses[i]); + } + } + } catch (IOException ioe) { + throw new InvalidMessageException("Failed to write message data"); + } + } + + /** + * + * + * @param bar + * + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws com.sshtools.j2ssh.transport.InvalidMessageException { + try { + int num = (int) bar.readInt(); + + if (num > 0) { + responses = new String[num]; + + for (int i = 0; i < responses.length; i++) { + responses[i] = bar.readString(); + } + } + } catch (IOException ioe) { + throw new InvalidMessageException("Failed to read message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthPKOK.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthPKOK.java new file mode 100644 index 0000000000000000000000000000000000000000..768085c9d3c9915daca2f8876f11486203ff91cb --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthPKOK.java @@ -0,0 +1,127 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class SshMsgUserAuthPKOK extends SshMessage { + /** */ + public final static int SSH_MSG_USERAUTH_PK_OK = 60; + private String algorithm; + private byte[] key; + + //private boolean ok; + + /** + * Creates a new SshMsgUserAuthPKOK object. + */ + public SshMsgUserAuthPKOK() { + super(SSH_MSG_USERAUTH_PK_OK); + } + + /** + * Creates a new SshMsgUserAuthPKOK object. + * + * @param ok + * @param algorithm + * @param key + */ + public SshMsgUserAuthPKOK( /*boolean ok,*/ + String algorithm, byte[] key) { + super(SSH_MSG_USERAUTH_PK_OK); + + //this.ok = ok; + this.algorithm = algorithm; + this.key = key; + } + + /** + * + * + * @return + */ + + /*public boolean isOk() { + return ok; + }*/ + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_PK_OK"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + //baw.write(ok ? 1 : 0); + baw.writeString(algorithm); + baw.writeBinaryString(key); + } catch (IOException ioe) { + throw new InvalidMessageException("Failed to write message data!"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + //ok = ((bar.read() == 1) ? true : false); + algorithm = bar.readString(); + key = bar.readBinaryString(); + } catch (IOException ioe) { + throw new InvalidMessageException("Failed to read message data!"); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthPwdChangeReq.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthPwdChangeReq.java new file mode 100644 index 0000000000000000000000000000000000000000..aeb7c23d0587e59409bf9c240c7399686df5ecb5 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthPwdChangeReq.java @@ -0,0 +1,125 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshMsgUserAuthPwdChangeReq extends SshMessage { + /** */ + public static final int SSH_MSG_USERAUTH_PWD_CHANGEREQ = 60; + private String prompt; + private String language; + + /** + * Creates a new SshMsgUserAuthPwdChangeReq object. + */ + public SshMsgUserAuthPwdChangeReq() { + super(SSH_MSG_USERAUTH_PWD_CHANGEREQ); + } + + /** + * Creates a new SshMsgUserAuthPwdChangeReq object. + * + * @param prompt + * @param language + */ + public SshMsgUserAuthPwdChangeReq(String prompt, String language) { + super(SSH_MSG_USERAUTH_PWD_CHANGEREQ); + this.prompt = prompt; + this.language = language; + } + + /** + * + * + * @return + */ + public String getPrompt() { + return prompt; + } + + /** + * + * + * @return + */ + public String getLanguage() { + return language; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_PWD_CHANGEREQ"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(prompt); + baw.writeString(language); + } catch (Exception ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + prompt = bar.readString(); + language = bar.readString(); + } catch (Exception ex) { + throw new InvalidMessageException(ex.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthRequest.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..ce94c5b849970587f0433881abb13182beb5c919 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthRequest.java @@ -0,0 +1,163 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.24 $ + */ +public class SshMsgUserAuthRequest extends SshMessage { + /** */ + public final static int SSH_MSG_USERAUTH_REQUEST = 50; + private String methodName; + private String serviceName; + private String username; + private byte[] requestData; + + /** + * Creates a new SshMsgUserAuthRequest object. + */ + public SshMsgUserAuthRequest() { + super(SSH_MSG_USERAUTH_REQUEST); + } + + /** + * Creates a new SshMsgUserAuthRequest object. + * + * @param username + * @param serviceName + * @param methodName + * @param requestData + */ + public SshMsgUserAuthRequest(String username, String serviceName, + String methodName, byte[] requestData) { + super(SSH_MSG_USERAUTH_REQUEST); + this.username = username; + this.serviceName = serviceName; + this.methodName = methodName; + this.requestData = requestData; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_REQUEST"; + } + + /** + * + * + * @return + */ + public String getMethodName() { + return methodName; + } + + /** + * + * + * @return + */ + public byte[] getRequestData() { + return requestData; + } + + /** + * + * + * @return + */ + public String getServiceName() { + return serviceName; + } + + /** + * + * + * @return + */ + public String getUsername() { + return username; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(username); + baw.writeString(serviceName); + baw.writeString(methodName); + + if (requestData != null) { + baw.write(requestData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + username = bar.readString(); + serviceName = bar.readString(); + methodName = bar.readString(); + + if (bar.available() > 0) { + requestData = new byte[bar.available()]; + bar.read(requestData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthSuccess.java b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthSuccess.java new file mode 100644 index 0000000000000000000000000000000000000000..3b889570d0a7b3d4bdf1208e136e41e56fff3cc2 --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/SshMsgUserAuthSuccess.java @@ -0,0 +1,81 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.23 $ + */ +public class SshMsgUserAuthSuccess extends SshMessage { + /** */ + protected final static int SSH_MSG_USERAUTH_SUCCESS = 52; + + /** + * Creates a new SshMsgUserAuthSuccess object. + */ + public SshMsgUserAuthSuccess() { + super(SSH_MSG_USERAUTH_SUCCESS); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_USERAUTH_SUCCESS"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + } +} diff --git a/src/com/sshtools/j2ssh/authentication/TerminatedStateException.java b/src/com/sshtools/j2ssh/authentication/TerminatedStateException.java new file mode 100644 index 0000000000000000000000000000000000000000..e43cb155f498dfbf232846aaf02aefe35dd6850e --- /dev/null +++ b/src/com/sshtools/j2ssh/authentication/TerminatedStateException.java @@ -0,0 +1,55 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.authentication; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class TerminatedStateException extends Exception { + private int state; + + /** + * Creates a new TerminatedStateException object. + * + * @param state + */ + public TerminatedStateException(int state) { + this.state = state; + } + + /** + * + * + * @return + */ + public int getState() { + return state; + } +} diff --git a/src/com/sshtools/j2ssh/configuration/ConfigurationContext.java b/src/com/sshtools/j2ssh/configuration/ConfigurationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..b42ba4ae624581155e17887102265e3c64e692c1 --- /dev/null +++ b/src/com/sshtools/j2ssh/configuration/ConfigurationContext.java @@ -0,0 +1,62 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.configuration; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface ConfigurationContext { + /** + * + * + * @param cls + * + * @return + */ + public boolean isConfigurationAvailable(Class cls); + + /** + * + * + * @param cls + * + * @return + * + * @throws ConfigurationException + */ + public Object getConfiguration(Class cls) throws ConfigurationException; + + /** + * + * + * @throws ConfigurationException + */ + public void initialize() throws ConfigurationException; +} diff --git a/src/com/sshtools/j2ssh/configuration/ConfigurationException.java b/src/com/sshtools/j2ssh/configuration/ConfigurationException.java new file mode 100644 index 0000000000000000000000000000000000000000..db075b8a1d6f1dab3ed82a4a730713bad8c4e42f --- /dev/null +++ b/src/com/sshtools/j2ssh/configuration/ConfigurationException.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.configuration; + +import com.sshtools.j2ssh.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ConfigurationException extends SshException { + /** + * Creates a new ConfigurationException object. + * + * @param msg + */ + public ConfigurationException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/configuration/ConfigurationLoader.java b/src/com/sshtools/j2ssh/configuration/ConfigurationLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..c52c6431d9e9913975dccea44eb0a02172d2f8c0 --- /dev/null +++ b/src/com/sshtools/j2ssh/configuration/ConfigurationLoader.java @@ -0,0 +1,491 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.configuration; + +import com.sshtools.j2ssh.util.ExtensionClassLoader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.InputStream; +import java.io.OutputStream; + +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.SecureRandom; +import java.security.Security; + +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.PropertyPermission; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.67 $ + */ +public class ConfigurationLoader { + private static Vector contexts = new Vector(); + private static SecureRandom rnd; + private static ExtensionClassLoader ext = null; + private static ClassLoader clsLoader = null; + private static Log log = LogFactory.getLog(ConfigurationLoader.class); + private static String homedir; + private static boolean initialized = false; + private static Object initializationLock = new Object(); + + static { + // Get the sshtools.home system property. If system properties can not be + // read then we are running in a sandbox + // try { + homedir = checkAndGetProperty("sshtools.home", + System.getProperty("java.home")); + + if ((homedir != null) && !homedir.endsWith(File.separator)) { + homedir += File.separator; + } + + rnd = new SecureRandom(); + rnd.nextInt(); + } + + /** + * + * + * @return + */ + public static SecureRandom getRND() { + return rnd; + } + + /** + * + * + * @param projectname + * @param versionFile + * + * @return + */ + public static String getVersionString(String projectname, String versionFile) { + Properties properties = new Properties(); + String version = projectname; + + try { + properties.load(loadFile(versionFile)); + + String project = projectname.toLowerCase(); + String major = properties.getProperty(project + ".version.major"); + String minor = properties.getProperty(project + ".version.minor"); + String build = properties.getProperty(project + ".version.build"); + String type = properties.getProperty(project + ".project.type"); + + if ((major != null) && (minor != null) && (build != null)) { + version += (" " + major + "." + minor + "." + build); + } + + if (type != null) { + version += (" " + type); + } + } catch (Exception e) { + } + + return version; + } + + /** + * + * + * @param property + * @param defaultValue + * + * @return + */ + public static String checkAndGetProperty(String property, + String defaultValue) { + // Check for access to sshtools.platform + try { + if (System.getSecurityManager() != null) { + AccessController.checkPermission(new PropertyPermission( + property, "read")); + } + + return System.getProperty(property, defaultValue); + } catch (AccessControlException ace) { + return defaultValue; + } + } + + /** + * + * + * @param force + * + * @throws ConfigurationException + */ + public static void initialize(boolean force) throws ConfigurationException { + initialize(force, new DefaultConfigurationContext()); + } + + /** + * <p> + * Initializes the J2SSH api with a specified configuration context. This + * method will attempt to load the Bouncycastle JCE if it detects the java + * version is 1.3.1. + * </p> + * + * @param force force the configuration to load even if a configuration + * already exists + * @param context the configuration context to load + * + * @throws ConfigurationException if the configuration is invalid or if a + * security provider is not available + */ + public static void initialize(boolean force, ConfigurationContext context) + throws ConfigurationException { + // } + try { + String javaversion = System.getProperty("java.version"); + log.info("JAVA version is " + javaversion); + + if (javaversion.startsWith("1.3")) { + boolean provider = false; + + for (int i = 0; i < Security.getProviders().length; i++) { + log.info(Security.getProviders()[i].getName() + + " security provider found"); + + if (Security.getProviders()[i].getClass().getName().equals("org.bouncycastle.jce.provider.BouncyCastleProvider")) { + provider = true; + } + } + + if (provider == false) { + log.info("Attempting to load the bouncycastle jce provider"); + + // Attempt to load a JCE Provider - replace or remove these statements + // depending upon how you want to initialize your JCE provider + Class cls; + cls = Class.forName( + "org.bouncycastle.jce.provider.BouncyCastleProvider"); + java.security.Security.addProvider((java.security.Provider) cls.newInstance()); + } + } + } catch (Exception ex) { + log.info("Failed to load the bouncycastle jce provider", ex); + + if (java.security.Security.getProviders().length <= 0) { + throw new ConfigurationException( + "There are no security providers available; install jce-jdk13-119.jar available from http://www.bouncycastle.org"); + } else { + log.info("An existing provider has been detected"); + } + } + + synchronized (initializationLock) { + if (initialized && !force) { + return; + } + + // } + context.initialize(); + contexts.add(context); + + if (ext == null) { + // We need to setup the dynamic class loading with the extension jars + ext = new ExtensionClassLoader(ConfigurationLoader.class.getClassLoader()); + + try { + // Jar files to add to the classpath + File dir = new File(homedir + "lib" + File.separator + + "ext"); + + // Filter for .jar files + FilenameFilter filter = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + }; + + // Get the list + File[] children = dir.listFiles(filter); + List classpath = new Vector(); + + if (children != null) { + for (int i = 0; i < children.length; i++) { + // Get filename of file or directory + log.info("Extension " + + children[i].getAbsolutePath() + + " being added to classpath"); + ext.add(children[i]); + } + } + } catch (AccessControlException ex) { + log.info( + "Cannot access lib/ext directory, extension classes will not be loaded"); + } + } + + initialized = true; + } + } + + /** + * + * + * @param cls + * + * @return + * + * @throws ConfigurationException + */ + public static boolean isConfigurationAvailable(Class cls) + throws ConfigurationException { + if (!initialized) { + initialize(false); + } + + if (contexts.size() > 0) { + Iterator it = contexts.iterator(); + + while (it.hasNext()) { + ConfigurationContext context = (ConfigurationContext) it.next(); + + if (context.isConfigurationAvailable(cls)) { + return true; + } + } + + return false; + } else { + return false; + } + } + + /** + * + * + * @param cls + * + * @return + * + * @throws ConfigurationException + */ + public static Object getConfiguration(Class cls) + throws ConfigurationException { + if (contexts.size() > 0) { + Iterator it = contexts.iterator(); + + while (it.hasNext()) { + ConfigurationContext context = (ConfigurationContext) it.next(); + + if (context.isConfigurationAvailable(cls)) { + return context.getConfiguration(cls); + } + } + } + + throw new ConfigurationException("No " + cls.getName() + + " configuration is available in this context"); + } + + /** + * + * + * @return + */ + public static String getConfigurationDirectory() { + return homedir + "conf/"; + } + + /** + * + * + * @param name + * + * @return + * + * @throws ClassNotFoundException + * @throws ConfigurationException + */ + public static Class getExtensionClass(String name) + throws ClassNotFoundException, ConfigurationException { + if (!initialized) { + initialize(false); + } + + if (ext == null) { + throw new ClassNotFoundException("Configuration not initialized"); + } + + return ext.loadClass(name); + } + + /** + * + * + * @return + */ + public static String getHomeDirectory() { + return homedir; + } + + /** + * + * + * @param clsLoader + */ + public static void setContextClassLoader(ClassLoader clsLoader) { + ConfigurationLoader.clsLoader = clsLoader; + } + + public static ExtensionClassLoader getExtensionClassLoader() { + return ext; + } + + public static String getExtensionPath() { + return homedir + "/lib/ext"; + } + + /** + * + * + * @return + */ + public static ClassLoader getContextClassLoader() { + return ConfigurationLoader.clsLoader; + } + + /** + * + * + * @return + */ + public static boolean isContextClassLoader() { + return (clsLoader != null); + } + + /** + * + * + * @param homedir + */ + public static void setHomeDirectory(String homedir) { + ConfigurationLoader.homedir = homedir.replace('\\', '/'); + + if (!ConfigurationLoader.homedir.endsWith("/")) { + ConfigurationLoader.homedir += "/"; + } + } + + /** + * + * + * @param filename + * + * @return + * + * @throws FileNotFoundException + */ + public static InputStream loadFile(String filename) + throws FileNotFoundException { + FileInputStream in; + + try { + in = new FileInputStream(getConfigurationDirectory() + filename); + + return in; + } catch (FileNotFoundException fnfe) { + } + + try { + in = new FileInputStream(homedir + filename); + + return in; + } catch (FileNotFoundException fnfe) { + } + + in = new FileInputStream(filename); + + return in; + } + + /** + * + * + * @param filename + * + * @return + * + * @throws FileNotFoundException + */ + public static OutputStream saveFile(String filename) + throws FileNotFoundException { + // Look for the file in the config directory + File f = new File(getConfigurationDirectory() + filename); + + if (f.exists()) { + // Yes its there so create an outputstream to it + return new FileOutputStream(f); + } else { + // Look for it absolute + f = new File(filename); + + if (f.exists()) { + return new FileOutputStream(filename); // yes so do absolute + } else { + // Determine whether the filename is absolute or not with a primitive check + return new FileOutputStream((filename.indexOf( + File.pathSeparator) >= 0) ? filename + : (getConfigurationDirectory() + + filename)); + } + } + } + + static class DefaultConfigurationContext implements ConfigurationContext { + public void initialize() throws ConfigurationException { + // Do nothing + } + + public boolean isConfigurationAvailable(Class cls) { + return false; + } + + public Object getConfiguration(Class cls) throws ConfigurationException { + throw new ConfigurationException( + "Default configuration does not contain " + cls.getName()); + } + } +} diff --git a/src/com/sshtools/j2ssh/configuration/ExtensionAlgorithm.java b/src/com/sshtools/j2ssh/configuration/ExtensionAlgorithm.java new file mode 100644 index 0000000000000000000000000000000000000000..303fdc0af408dbccf182e05533fd79ef271ca283 --- /dev/null +++ b/src/com/sshtools/j2ssh/configuration/ExtensionAlgorithm.java @@ -0,0 +1,80 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.configuration; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ExtensionAlgorithm { + private String name; + private String implClass; + + /** + * Creates a new ExtensionAlgorithm object. + */ + public ExtensionAlgorithm() { + } + + /** + * + * + * @return + */ + public String getAlgorithmName() { + return name; + } + + /** + * + * + * @return + */ + public String getImplementationClass() { + return implClass; + } + + /** + * + * + * @param name + */ + public void setAlgorithmName(String name) { + this.name = name; + } + + /** + * + * + * @param implClass + */ + public void setImplementationClass(String implClass) { + this.implClass = implClass; + } +} diff --git a/src/com/sshtools/j2ssh/configuration/SshAPIConfiguration.java b/src/com/sshtools/j2ssh/configuration/SshAPIConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..31160b412c832f8e64d96bda7d632795261186cb --- /dev/null +++ b/src/com/sshtools/j2ssh/configuration/SshAPIConfiguration.java @@ -0,0 +1,142 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.configuration; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.27 $ + */ +public interface SshAPIConfiguration { + /** + * + * + * @return + */ + public List getCompressionExtensions(); + + /** + * + * + * @return + */ + public List getCipherExtensions(); + + /** + * + * + * @return + */ + public List getMacExtensions(); + + /** + * + * + * @return + */ + public List getAuthenticationExtensions(); + + /** + * + * + * @return + */ + public List getPublicKeyExtensions(); + + /** + * + * + * @return + */ + public List getKeyExchangeExtensions(); + + /** + * + * + * @return + */ + public String getDefaultCipher(); + + /** + * + * + * @return + */ + public String getDefaultMac(); + + /** + * + * + * @return + */ + public String getDefaultCompression(); + + /** + * + * + * @return + */ + public String getDefaultPublicKey(); + + /** + * + * + * @return + */ + public String getDefaultKeyExchange(); + + /** + * + * + * @return + */ + public String getDefaultPublicKeyFormat(); + + /** + * + * + * @return + */ + public String getDefaultPrivateKeyFormat(); + + /** + * + * + * @return + */ + public List getPublicKeyFormats(); + + /** + * + * + * @return + */ + public List getPrivateKeyFormats(); +} diff --git a/src/com/sshtools/j2ssh/configuration/SshConnectionProperties.java b/src/com/sshtools/j2ssh/configuration/SshConnectionProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..5f7590a8dc9129c14c6951e3f5f32dc8004cff73 --- /dev/null +++ b/src/com/sshtools/j2ssh/configuration/SshConnectionProperties.java @@ -0,0 +1,474 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.configuration; + +import com.sshtools.j2ssh.forwarding.ForwardingConfiguration; +import com.sshtools.j2ssh.transport.cipher.SshCipherFactory; +import com.sshtools.j2ssh.transport.compression.SshCompressionFactory; +import com.sshtools.j2ssh.transport.hmac.SshHmacFactory; +import com.sshtools.j2ssh.transport.kex.SshKeyExchangeFactory; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.HashMap; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.27 $ + */ +public class SshConnectionProperties { + private static Log log = LogFactory.getLog(SshConnectionProperties.class); + + /** */ + public static final int USE_STANDARD_SOCKET = 1; + + /** */ + public static final int USE_HTTP_PROXY = 2; + + /** */ + public static final int USE_SOCKS4_PROXY = 3; + + /** */ + public static final int USE_SOCKS5_PROXY = 4; + + /** */ + protected int transportProvider = USE_STANDARD_SOCKET; + + /** */ + protected String proxyHostname; + + /** */ + protected int proxyPort; + + /** */ + protected String proxyUsername; + + /** */ + protected String proxyPassword; + + /** */ + protected String host; + + /** */ + protected String prefDecryption = SshCipherFactory.getDefaultCipher(); + + /** */ + protected String prefEncryption = SshCipherFactory.getDefaultCipher(); + + /** */ + protected String prefKex = SshKeyExchangeFactory.getDefaultKeyExchange(); + + /** */ + protected String prefPK = SshKeyPairFactory.getDefaultPublicKey(); + + /** */ + protected String prefRecvComp = SshCompressionFactory.getDefaultCompression(); + + /** */ + protected String prefRecvMac = SshHmacFactory.getDefaultHmac(); + + /** */ + protected String prefSendComp = SshCompressionFactory.getDefaultCompression(); + + /** */ + protected String prefSendMac = SshHmacFactory.getDefaultHmac(); + + /** */ + protected String username; + + /** */ + protected int port = 22; + protected Map localForwardings = new HashMap(); + protected Map remoteForwardings = new HashMap(); + protected boolean forwardingAutoStart = false; + + /** + * Creates a new SshConnectionProperties object. + */ + public SshConnectionProperties() { + } + + /** + * + * + * @param host + */ + public void setHost(String host) { + this.host = host; + } + + /** + * + * + * @return + */ + public String getHost() { + return host; + } + + /** + * + * + * @param port + */ + public void setPort(int port) { + this.port = port; + } + + /** + * + * + * @return + */ + public int getPort() { + return port; + } + + /** + * + * + * @return + */ + public int getTransportProvider() { + return transportProvider; + } + + /** + * + * + * @param name + */ + public void setTransportProviderString(String name) { + if (name != null) { + if (name.equalsIgnoreCase("http")) { + transportProvider = USE_HTTP_PROXY; + } else if (name.equalsIgnoreCase("socks4")) { + transportProvider = USE_SOCKS4_PROXY; + } else if (name.equalsIgnoreCase("socks5")) { + transportProvider = USE_SOCKS5_PROXY; + } else { + transportProvider = USE_STANDARD_SOCKET; + } + } else { + transportProvider = USE_STANDARD_SOCKET; + } + } + + /** + * + * + * @return + */ + public String getTransportProviderString() { + if (transportProvider == USE_HTTP_PROXY) { + return "http"; + } else if (transportProvider == USE_SOCKS4_PROXY) { + return "socks4"; + } else if (transportProvider == USE_SOCKS5_PROXY) { + return "socks5"; + } else { + return "socket"; + } + } + + /** + * + * + * @return + */ + public String getProxyHost() { + return proxyHostname; + } + + public void removeAllForwardings() { + localForwardings.clear(); + remoteForwardings.clear(); + } + + /** + * + * + * @return + */ + public int getProxyPort() { + return proxyPort; + } + + /** + * + * + * @return + */ + public String getProxyUsername() { + return proxyUsername; + } + + /** + * + * + * @return + */ + public String getProxyPassword() { + return proxyPassword; + } + + /** + * + * + * @param transportProvider + */ + public void setTransportProvider(int transportProvider) { + this.transportProvider = transportProvider; + } + + /** + * + * + * @param proxyHostname + */ + public void setProxyHost(String proxyHostname) { + this.proxyHostname = proxyHostname; + } + + /** + * + * + * @param proxyPort + */ + public void setProxyPort(int proxyPort) { + this.proxyPort = proxyPort; + } + + /** + * + * + * @param proxyUsername + */ + public void setProxyUsername(String proxyUsername) { + this.proxyUsername = proxyUsername; + } + + /** + * + * + * @param proxyPassword + */ + public void setProxyPassword(String proxyPassword) { + this.proxyPassword = proxyPassword; + } + + /** + * + * + * @param pref + */ + public void setPrefCSComp(String pref) { + prefSendComp = pref; + } + + /** + * + * + * @return + */ + public String getPrefCSComp() { + return prefSendComp; + } + + /** + * + * + * @param pref + */ + public void setPrefCSEncryption(String pref) { + prefEncryption = pref; + } + + /** + * + * + * @return + */ + public String getPrefCSEncryption() { + return prefEncryption; + } + + /** + * + * + * @param pref + */ + public void setPrefCSMac(String pref) { + prefSendMac = pref; + } + + /** + * + * + * @return + */ + public String getPrefCSMac() { + return prefSendMac; + } + + /** + * + * + * @param pref + */ + public void setPrefKex(String pref) { + prefKex = pref; + } + + /** + * + * + * @return + */ + public String getPrefKex() { + return prefKex; + } + + /** + * + * + * @param pref + */ + public void setPrefPublicKey(String pref) { + prefPK = pref; + } + + /** + * + * + * @return + */ + public String getPrefPublicKey() { + return prefPK; + } + + /** + * + * + * @param pref + */ + public void setPrefSCComp(String pref) { + prefRecvComp = pref; + } + + /** + * + * + * @return + */ + public String getPrefSCComp() { + return prefRecvComp; + } + + /** + * + * + * @param pref + */ + public void setPrefSCEncryption(String pref) { + prefDecryption = pref; + } + + /** + * + * + * @return + */ + public String getPrefSCEncryption() { + return prefDecryption; + } + + public Map getLocalForwardings() { + return localForwardings; + } + + public Map getRemoteForwardings() { + return remoteForwardings; + } + + public void addLocalForwarding(ForwardingConfiguration cf) { + localForwardings.put(cf.getName(), cf); + } + + public void addRemoteForwarding(ForwardingConfiguration cf) { + remoteForwardings.put(cf.getName(), cf); + } + + public boolean getForwardingAutoStartMode() { + return forwardingAutoStart; + } + + public void setForwardingAutoStartMode(boolean forwardingAutoStart) { + this.forwardingAutoStart = forwardingAutoStart; + } + + /** + * + * + * @param pref + */ + public void setPrefSCMac(String pref) { + prefRecvMac = pref; + } + + /** + * + * + * @return + */ + public String getPrefSCMac() { + return prefRecvMac; + } + + /** + * + * + * @param username + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * + * + * @return + */ + public String getUsername() { + return username; + } +} diff --git a/src/com/sshtools/j2ssh/connection/BindingChannel.java b/src/com/sshtools/j2ssh/connection/BindingChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..e7ac4ec150fd5278f98e3fae0dfb992ebcf197a6 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/BindingChannel.java @@ -0,0 +1,228 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.Iterator; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public abstract class BindingChannel extends Channel { + private Log log = LogFactory.getLog(BindingChannel.class); + + /** */ + protected BindingChannel boundChannel; + + /** */ + protected Vector messages = new Vector(); + + /** + * + * + * @return + */ + public boolean isBound() { + return boundChannel != null; + } + + /** + * + * + * @param boundChannel + * + * @throws IOException + */ + public void bindChannel(BindingChannel boundChannel) + throws IOException { + if (boundChannel == null) { + throw new IOException("[" + getName() + + "] Bound channel cannot be null"); + } + + if (isBound()) { + throw new IOException("[" + getName() + + "] This channel is already bound to another channel [" + + boundChannel.getName() + "]"); + } + + this.boundChannel = boundChannel; + + if (!boundChannel.isBound()) { + boundChannel.bindChannel(this); + + synchronized (messages) { + if (boundChannel.isOpen() && (messages.size() > 0)) { + sendOutstandingMessages(); + } + } + } else { + if (!boundChannel.boundChannel.equals(this)) { + throw new IOException("[" + getName() + + "] Channel is already bound to an another channel [" + + boundChannel.boundChannel.getName() + "]"); + } + } + } + + private void sendOutstandingMessages() throws IOException { + // Send the outstanding messages + if (boundChannel == null) { + return; + } + + synchronized (messages) { + Iterator it = messages.iterator(); + + while (it.hasNext()) { + Object obj = it.next(); + + if (obj instanceof SshMsgChannelData) { + boundChannel.sendChannelData(((SshMsgChannelData) obj).getChannelData()); + } else if (obj instanceof SshMsgChannelExtendedData) { + boundChannel.sendChannelExtData(((SshMsgChannelExtendedData) obj).getDataTypeCode(), + ((SshMsgChannelExtendedData) obj).getChannelData()); + } else { + throw new IOException("[" + getName() + + "] Invalid message type in pre bound message list!"); + } + } + + messages.clear(); + } + } + + /** + * + * + * @param msg + * + * @throws java.io.IOException + */ + protected void onChannelExtData(SshMsgChannelExtendedData msg) + throws java.io.IOException { + synchronized (messages) { + if (boundChannel != null) { + if (boundChannel.isOpen()) { + boundChannel.sendChannelExtData(msg.getDataTypeCode(), + msg.getChannelData()); + } else { + messages.add(msg); + } + } + } + } + + /** + * + * + * @param msg + * + * @throws java.io.IOException + */ + protected void onChannelData(SshMsgChannelData msg) + throws java.io.IOException { + synchronized (messages) { + if (boundChannel != null) { + if (boundChannel.isOpen()) { + boundChannel.sendChannelData(msg.getChannelData()); + } else { + messages.add(msg); + } + } + } + } + + /*public void setLocalEOF() throws IOException { + synchronized(state) { + super.setLocalEOF(); + if (!boundChannel.isRemoteEOF()) { + log.info("onLocalEOF [" + getName() + "] is setting " + boundChannel.getName() + " to EOF"); + boundChannel.setRemoteEOF(); + //boundChannel.setLocalEOF(); + } + } + }*/ + protected void setRemoteEOF() throws IOException { + synchronized (state) { + super.setRemoteEOF(); + + if (!boundChannel.isLocalEOF()) { + log.info("onRemoteEOF [" + getName() + "] is setting " + + boundChannel.getName() + " to EOF"); + boundChannel.setLocalEOF(); + + //boundChannel.setRemoteEOF(); + } + } + } + + /** + * + * + * @throws java.io.IOException + */ + protected void onChannelEOF() throws java.io.IOException { + } + + /** + * + * + * @throws java.io.IOException + */ + protected void onChannelClose() throws java.io.IOException { + /*synchronized(state) { + if (boundChannel != null) { + if (boundChannel.isOpen()) + boundChannel.close(); + } + }*/ + } + + /** + * + * + * @throws java.io.IOException + */ + protected void onChannelOpen() throws java.io.IOException { + synchronized (messages) { + if (boundChannel != null) { + if (boundChannel.isOpen() && (messages.size() > 0)) { + sendOutstandingMessages(); + } + } + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/Channel.java b/src/com/sshtools/j2ssh/connection/Channel.java new file mode 100644 index 0000000000000000000000000000000000000000..e2cc2f123eea92bd0e5526e43e7b4bb64fa40c01 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/Channel.java @@ -0,0 +1,647 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.Iterator; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.74 $ + */ +public abstract class Channel { + private static Log log = LogFactory.getLog(Channel.class); + + /** */ + protected ChannelDataWindow localWindow = new ChannelDataWindow(); + + /** */ + protected ChannelDataWindow remoteWindow = new ChannelDataWindow(); + + /** */ + protected ConnectionProtocol connection; + + /** */ + protected long localChannelId; + + /** */ + protected long localPacketSize; + + /** */ + protected long remoteChannelId; + + /** */ + protected long remotePacketSize; + + /** */ + protected ChannelState state = new ChannelState(); + private boolean isClosed = false; + private boolean isLocalEOF = false; + private boolean isRemoteEOF = false; + private boolean localHasClosed = false; + private boolean remoteHasClosed = false; + private String name = "Unnamed Channel"; + private Vector eventListeners = new Vector(); + + /** + * Creates a new Channel object. + */ + public Channel() { + this.localPacketSize = getMaximumPacketSize(); + this.localWindow.increaseWindowSpace(getMaximumWindowSpace()); + } + + /** + * + * + * @return + */ + public abstract byte[] getChannelOpenData(); + + /** + * + * + * @return + */ + public abstract byte[] getChannelConfirmationData(); + + /** + * + * + * @return + */ + public abstract String getChannelType(); + + /** + * + * + * @return + */ + protected abstract int getMinimumWindowSpace(); + + /** + * + * + * @return + */ + protected abstract int getMaximumWindowSpace(); + + /** + * + * + * @return + */ + protected abstract int getMaximumPacketSize(); + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected abstract void onChannelData(SshMsgChannelData msg) + throws IOException; + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void processChannelData(SshMsgChannelData msg) + throws IOException { + synchronized (state) { + if (!isClosed()) { + if (msg.getChannelDataLength() > localWindow.getWindowSpace()) { + throw new IOException( + "More data recieved than is allowed by the channel data window [" + + name + "]"); + } + + long windowSpace = localWindow.consumeWindowSpace(msg.getChannelData().length); + + if (windowSpace < getMinimumWindowSpace()) { + if (log.isDebugEnabled()) { + log.debug("Channel " + String.valueOf(localChannelId) + + " requires more window space [" + name + "]"); + } + + windowSpace = getMaximumWindowSpace() - windowSpace; + log.debug("Requesting connection protocol increase window"); + connection.sendChannelWindowAdjust(this, windowSpace); + localWindow.increaseWindowSpace(windowSpace); + } + + onChannelData(msg); + + Iterator it = eventListeners.iterator(); + ChannelEventListener eventListener; + + while (it.hasNext()) { + eventListener = (ChannelEventListener) it.next(); + + if (eventListener != null) { + eventListener.onDataReceived(this, msg.getChannelData()); + } + } + } else { + throw new IOException( + "Channel data received but channel is closed [" + name + + "]"); + } + } + } + + /** + * + * + * @return + */ + public boolean isClosed() { + synchronized (state) { + return state.getValue() == ChannelState.CHANNEL_CLOSED; + } + } + + /** + * + * + * @return + */ + public boolean isOpen() { + synchronized (state) { + return state.getValue() == ChannelState.CHANNEL_OPEN; + } + } + + /** + * + * + * @param data + * + * @throws IOException + */ + protected + /*synchronized*/ void sendChannelData(byte[] data) throws IOException { + if (!connection.isConnected()) { + throw new IOException("The connection has been closed [" + name + + "]"); + } + + if (!isClosed()) { + connection.sendChannelData(this, data); + + Iterator it = eventListeners.iterator(); + ChannelEventListener eventListener; + + while (it.hasNext()) { + eventListener = (ChannelEventListener) it.next(); + + if (eventListener != null) { + eventListener.onDataSent(this, data); + } + } + } else { + throw new IOException("The channel is closed [" + name + "]"); + } + } + + /** + * + * + * @param type + * @param data + * + * @throws IOException + */ + protected + /*synchronized*/ void sendChannelExtData(int type, byte[] data) throws IOException { + if (!connection.isConnected()) { + throw new IOException("The connection has been closed [" + name + + "]"); + } + + if (!isClosed()) { + connection.sendChannelExtData(this, type, data); + + Iterator it = eventListeners.iterator(); + ChannelEventListener eventListener; + + while (it.hasNext()) { + eventListener = (ChannelEventListener) it.next(); + + if (eventListener != null) { + eventListener.onDataSent(this, data); + } + } + } else { + throw new IOException("The channel is closed [" + name + "]"); + } + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected abstract void onChannelExtData(SshMsgChannelExtendedData msg) + throws IOException; + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void processChannelData(SshMsgChannelExtendedData msg) + throws IOException { + synchronized (state) { + if (msg.getChannelData().length > localWindow.getWindowSpace()) { + throw new IOException( + "More data recieved than is allowed by the channel data window [" + + name + "]"); + } + + long windowSpace = localWindow.consumeWindowSpace(msg.getChannelData().length); + + if (windowSpace < getMinimumWindowSpace()) { + if (log.isDebugEnabled()) { + log.debug("Channel " + String.valueOf(localChannelId) + + " requires more window space [" + name + "]"); + } + + windowSpace = getMaximumWindowSpace() - windowSpace; + connection.sendChannelWindowAdjust(this, windowSpace); + localWindow.increaseWindowSpace(windowSpace); + } + + onChannelExtData(msg); + + Iterator it = eventListeners.iterator(); + ChannelEventListener eventListener; + + while (it.hasNext()) { + eventListener = (ChannelEventListener) it.next(); + + if (eventListener != null) { + eventListener.onDataReceived(this, msg.getChannelData()); + } + } + } + } + + /** + * + * + * @return + */ + public long getLocalChannelId() { + return localChannelId; + } + + /** + * + * + * @return + */ + public long getLocalPacketSize() { + return localPacketSize; + } + + /** + * + * + * @return + */ + public ChannelDataWindow getLocalWindow() { + return localWindow; + } + + /** + * + * + * @return + */ + public long getRemoteChannelId() { + return remoteChannelId; + } + + /** + * + * + * @return + */ + public long getRemotePacketSize() { + return remotePacketSize; + } + + /** + * + * + * @return + */ + public ChannelDataWindow getRemoteWindow() { + return remoteWindow; + } + + /** + * + * + * @return + */ + public ChannelState getState() { + return state; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + synchronized (state) { + if (isOpen()) { + if ((connection != null) && !localHasClosed && + connection.isConnected()) { + connection.closeChannel(this); + } + + localHasClosed = true; + + if (log.isDebugEnabled()) { + log.debug("Connection is " + + ((connection == null) ? "null" + : (connection.isConnected() + ? "connected" : "not connected"))); + } + + if (remoteHasClosed || + ((connection == null) || !connection.isConnected())) { + log.info("Finializing channel close"); + finalizeClose(); + } + } + } + } + + /** + * + * + * @throws IOException + */ + protected void remoteClose() throws IOException { + log.info("Remote side is closing channel"); + + synchronized (state) { + remoteHasClosed = true; + close(); + } + } + + /** + * + * + * @throws IOException + */ + protected void finalizeClose() throws IOException { + synchronized (state) { + state.setValue(ChannelState.CHANNEL_CLOSED); + onChannelClose(); + + Iterator it = eventListeners.iterator(); + ChannelEventListener eventListener; + + while (it.hasNext()) { + eventListener = (ChannelEventListener) it.next(); + + if (eventListener != null) { + eventListener.onChannelClose(this); + } + } + + if (connection != null) { + connection.freeChannel(this); + } + } + } + + /** + * + * + * @throws IOException + */ + public void setLocalEOF() throws IOException { + synchronized (state) { + isLocalEOF = true; + connection.sendChannelEOF(this); + } + } + + /** + * + * + * @return + */ + public boolean isLocalEOF() { + return isLocalEOF; + } + + /** + * + * + * @return + */ + public boolean isRemoteEOF() { + return isRemoteEOF; + } + + /** + * + * + * @throws IOException + */ + protected void setRemoteEOF() throws IOException { + synchronized (state) { + isRemoteEOF = true; + onChannelEOF(); + + Iterator it = eventListeners.iterator(); + ChannelEventListener eventListener; + + while (it.hasNext()) { + eventListener = (ChannelEventListener) it.next(); + + if (eventListener != null) { + eventListener.onChannelEOF(this); + } + } + } + } + + /** + * + * + * @param eventListener + */ + public void addEventListener(ChannelEventListener eventListener) { + eventListeners.add(eventListener); + } + + /** + * + * + * @param connection + * @param localChannelId + * @param senderChannelId + * @param initialWindowSize + * @param maximumPacketSize + * + * @throws IOException + */ + protected void init(ConnectionProtocol connection, long localChannelId, + long senderChannelId, long initialWindowSize, long maximumPacketSize) + throws IOException { + this.localChannelId = localChannelId; + this.remoteChannelId = senderChannelId; + this.remotePacketSize = maximumPacketSize; + this.remoteWindow.increaseWindowSpace(initialWindowSize); + this.connection = connection; + + synchronized (state) { + state.setValue(ChannelState.CHANNEL_OPEN); + } + } + + /** + * + * + * @throws IOException + */ + protected void open() throws IOException { + synchronized (state) { + state.setValue(ChannelState.CHANNEL_OPEN); + onChannelOpen(); + + Iterator it = eventListeners.iterator(); + ChannelEventListener eventListener; + + while (it.hasNext()) { + eventListener = (ChannelEventListener) it.next(); + + if (eventListener != null) { + eventListener.onChannelOpen(this); + } + } + } + } + + /** + * + * + * @param connection + * @param localChannelId + * @param senderChannelId + * @param initialWindowSize + * @param maximumPacketSize + * @param eventListener + * + * @throws IOException + */ + protected void init(ConnectionProtocol connection, long localChannelId, + long senderChannelId, long initialWindowSize, long maximumPacketSize, + ChannelEventListener eventListener) throws IOException { + if (eventListener != null) { + addEventListener(eventListener); + } + + init(connection, localChannelId, senderChannelId, initialWindowSize, + maximumPacketSize); + } + + /** + * + * + * @throws IOException + */ + protected abstract void onChannelClose() throws IOException; + + /** + * + * + * @throws IOException + */ + protected abstract void onChannelEOF() throws IOException; + + /** + * + * + * @throws IOException + */ + protected abstract void onChannelOpen() throws IOException; + + /** + * + * + * @param requestType + * @param wantReply + * @param requestData + * + * @throws IOException + */ + protected abstract void onChannelRequest(String requestType, + boolean wantReply, byte[] requestData) throws IOException; + + /** + * + * + * @param name + */ + public void setName(String name) { + this.name = name; + } + + /** + * + * + * @return + */ + public String getName() { + return name; + } +} diff --git a/src/com/sshtools/j2ssh/connection/ChannelDataWindow.java b/src/com/sshtools/j2ssh/connection/ChannelDataWindow.java new file mode 100644 index 0000000000000000000000000000000000000000..7c1c156f80a2e23cf61197a8ff60b45750d83bc3 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ChannelDataWindow.java @@ -0,0 +1,106 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class ChannelDataWindow { + private static Log log = LogFactory.getLog(ChannelDataWindow.class); + long windowSpace = 0; + + /** + * Creates a new ChannelDataWindow object. + */ + public ChannelDataWindow() { + } + + /** + * + * + * @return + */ + public synchronized long getWindowSpace() { + return windowSpace; + } + + /** + * + * + * @param count + * + * @return + */ + public synchronized long consumeWindowSpace(int count) { + if (windowSpace < count) { + waitForWindowSpace(count); + } + + windowSpace -= count; + + return windowSpace; + } + + /** + * + * + * @param count + */ + public synchronized void increaseWindowSpace(long count) { + if (log.isDebugEnabled()) { + log.debug("Increasing window space by " + String.valueOf(count)); + } + + windowSpace += count; + notifyAll(); + } + + /** + * + * + * @param minimum + */ + public synchronized void waitForWindowSpace(int minimum) { + if (log.isDebugEnabled()) { + log.debug("Waiting for " + String.valueOf(minimum) + + " bytes of window space"); + } + + while (windowSpace < minimum) { + try { + wait(50); + } catch (InterruptedException e) { + } + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/ChannelEventAdapter.java b/src/com/sshtools/j2ssh/connection/ChannelEventAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..b5c6c1bff111199e53dc3dae98a718a5cb17db24 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ChannelEventAdapter.java @@ -0,0 +1,97 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + + +/** + * <p> + * Title: + * </p> + * + * <p> + * Description: + * </p> + * + * <p> + * Copyright: Copyright (c) 2003 + * </p> + * + * <p> + * Company: + * </p> + * + * @author Lee David Painter + * @version $Id: ChannelEventAdapter.java,v 1.8 2003/09/11 15:35:06 martianx Exp $ + */ +public abstract class ChannelEventAdapter implements ChannelEventListener { + /** + * Creates a new ChannelEventAdapter object. + */ + public ChannelEventAdapter() { + } + + /** + * + * + * @param channel + */ + public void onChannelOpen(Channel channel) { + } + + /** + * + * + * @param channel + */ + public void onChannelEOF(Channel channel) { + } + + /** + * + * + * @param channel + */ + public void onChannelClose(Channel channel) { + } + + /** + * + * + * @param channel + * @param data + */ + public void onDataReceived(Channel channel, byte[] data) { + } + + /** + * + * + * @param channel + * @param data + */ + public void onDataSent(Channel channel, byte[] data) { + } +} diff --git a/src/com/sshtools/j2ssh/connection/ChannelEventListener.java b/src/com/sshtools/j2ssh/connection/ChannelEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..76e25487c39eac457b6e52db8d5ed69ccb88cd61 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ChannelEventListener.java @@ -0,0 +1,72 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public interface ChannelEventListener { + /** + * + * + * @param channel + */ + public void onChannelOpen(Channel channel); + + /** + * + * + * @param channel + */ + public void onChannelEOF(Channel channel); + + /** + * + * + * @param channel + */ + public void onChannelClose(Channel channel); + + /** + * + * + * @param channel + * @param data + */ + public void onDataReceived(Channel channel, byte[] data); + + /** + * + * + * @param channel + * @param data + */ + public void onDataSent(Channel channel, byte[] data); +} diff --git a/src/com/sshtools/j2ssh/connection/ChannelFactory.java b/src/com/sshtools/j2ssh/connection/ChannelFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..cd6af8394a80178256758c0b68d497e2bc8b5ba4 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ChannelFactory.java @@ -0,0 +1,39 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public interface ChannelFactory { + //public List getChannelType(); + public Channel createChannel(String channelType, byte[] requestData) + throws InvalidChannelException; +} diff --git a/src/com/sshtools/j2ssh/connection/ChannelInputStream.java b/src/com/sshtools/j2ssh/connection/ChannelInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..d1e3cd857aa57b3239477d048c29f811aa77da43 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ChannelInputStream.java @@ -0,0 +1,309 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.transport.MessageNotAvailableException; +import com.sshtools.j2ssh.transport.MessageStoreEOFException; +import com.sshtools.j2ssh.transport.SshMessageStore; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.35 $ + */ +public class ChannelInputStream extends InputStream { + private static Log log = LogFactory.getLog(ChannelInputStream.class); + int[] filter; + byte[] msgdata; + int currentPos = 0; + private SshMessageStore messageStore; + private Integer type = null; + private int interrupt = 5000; + private boolean isBlocking = false; + private Object lock = new Object(); + private Thread blockingThread = null; + + /** + * Creates a new ChannelInputStream object. + * + * @param messageStore + * @param type + */ + public ChannelInputStream(SshMessageStore messageStore, Integer type) { + this.messageStore = messageStore; + filter = new int[1]; + this.type = type; + + if (type != null) { + filter[0] = SshMsgChannelExtendedData.SSH_MSG_CHANNEL_EXTENDED_DATA; + } else { + filter[0] = SshMsgChannelData.SSH_MSG_CHANNEL_DATA; + } + } + + /** + * Creates a new ChannelInputStream object. + * + * @param messageStore + */ + public ChannelInputStream(SshMessageStore messageStore) { + this(messageStore, null); + } + + /** + * + * + * @return + */ + public int available() { + int available = 0; + + if (msgdata != null) { + available = msgdata.length - currentPos; + + if (log.isDebugEnabled() && (available > 0)) { + log.debug(String.valueOf(available) + + " bytes of channel data available"); + } + + available = (available >= 0) ? available : 0; + } + + if (available == 0) { + try { + if (type != null) { + SshMsgChannelExtendedData msg = (SshMsgChannelExtendedData) messageStore.peekMessage(filter); + available = msg.getChannelData().length; + } else { + SshMsgChannelData msg = (SshMsgChannelData) messageStore.peekMessage(filter); + available = msg.getChannelData().length; + } + + if (log.isDebugEnabled()) { + log.debug(String.valueOf(available) + + " bytes of channel data available"); + } + } catch (MessageStoreEOFException mse) { + log.debug("No bytes available since the MessageStore is EOF"); + available = -1; + } catch (MessageNotAvailableException mna) { + available = 0; + } catch (InterruptedException ex) { + log.info("peekMessage was interrupted, no data available!"); + available = 0; + } + } + + return available; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + log.info("Closing ChannelInputStream"); + messageStore.close(); + } + + /** + * + * + * @return + */ + public boolean isClosed() { + return messageStore.isClosed(); + } + + /** + * + * + * @param interrupt + */ + public void setBlockInterrupt(int interrupt) { + this.interrupt = (interrupt < 1000) ? 1000 : interrupt; + } + + /** + * + */ + public void interrupt() { + messageStore.breakWaiting(); + } + + /** + * + * + * @return + * + * @throws java.io.IOException + * @throws InterruptedIOException + */ + public int read() throws java.io.IOException { + try { + block(); + + return msgdata[currentPos++] & 0xFF; + } catch (MessageStoreEOFException mse) { + return -1; + } catch (InterruptedException ex) { + throw new InterruptedIOException( + "The thread was interrupted whilst waiting for channel data"); + } + } + + /** + * + * + * @param b + * @param off + * @param len + * + * @return + * + * @throws IOException + * @throws IOException + */ + public int read(byte[] b, int off, int len) throws IOException { + try { + block(); + + int actual = available(); + + if (actual > len) { + actual = len; + } + + if (actual > 0) { + System.arraycopy(msgdata, currentPos, b, off, actual); + currentPos += actual; + } + + return actual; + } catch (MessageStoreEOFException mse) { + return -1; + } catch (InterruptedException ex) { + throw new InterruptedIOException( + "The thread was interrupted whilst waiting for channel data"); + } + } + + private void block() + throws MessageStoreEOFException, InterruptedException, IOException { + if (msgdata == null) { + collectNextMessage(); + } + + if (currentPos >= msgdata.length) { + collectNextMessage(); + } + } + + private void startBlockingOperation() throws IOException { + synchronized (lock) { + if (isBlocking) { + throw new IOException((("Cannot read from InputStream! " + + blockingThread) == null) ? "**NULL THREAD**" + : (blockingThread.getName() + + " is currently performing a blocking operation")); + } + + log.debug("Starting blocking operation"); + blockingThread = Thread.currentThread(); + isBlocking = true; + } + } + + private void stopBlockingOperation() throws IOException { + synchronized (lock) { + log.debug("Completed blocking operation"); + blockingThread = null; + isBlocking = false; + } + } + + private void collectNextMessage() + throws MessageStoreEOFException, InterruptedException, IOException { + // Collect the next message + startBlockingOperation(); + + try { + if (type != null) { + SshMsgChannelExtendedData msg = null; + + while ((msg == null) && !isClosed()) { + try { + log.debug("Waiting for extended channel data"); + msg = (SshMsgChannelExtendedData) messageStore.getMessage(filter, + interrupt); + } catch (MessageNotAvailableException ex) { + // Ignore the timeout but this allows us to review the + // InputStreams state once in a while + } + } + + if (msg != null) { + msgdata = msg.getChannelData(); + currentPos = 0; + } else { + throw new MessageStoreEOFException(); + } + } else { + SshMsgChannelData msg = null; + + while ((msg == null) && !isClosed()) { + try { + log.debug("Waiting for channel data"); + msg = (SshMsgChannelData) messageStore.getMessage(filter, + interrupt); + } catch (MessageNotAvailableException ex1) { + // Ignore the timeout but this allows us to review the + // InputStreams state once in a while + } + } + + if (msg != null) { + msgdata = msg.getChannelData(); + currentPos = 0; + } else { + throw new MessageStoreEOFException(); + } + } + } finally { + stopBlockingOperation(); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/ChannelOutputStream.java b/src/com/sshtools/j2ssh/connection/ChannelOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..42f3132a9704d7431d8f3535807f08ea1d07f88e --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ChannelOutputStream.java @@ -0,0 +1,160 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.OutputStream; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.27 $ + */ +public class ChannelOutputStream extends OutputStream { + private static Log log = LogFactory.getLog(ChannelOutputStream.class); + private Channel channel; + private boolean isClosed = false; + private Integer type = null; + + /** + * Creates a new ChannelOutputStream object. + * + * @param channel + * @param type + */ + public ChannelOutputStream(Channel channel, Integer type) { + this.channel = channel; + this.type = type; + } + + /** + * Creates a new ChannelOutputStream object. + * + * @param channel + */ + public ChannelOutputStream(Channel channel) { + this(channel, null); + } + + /** + * + * + * @return + */ + public boolean isClosed() { + return isClosed; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + log.info("Closing ChannelOutputStream"); + isClosed = true; + + // Send an EOF if the channel is not closed + if (!channel.isClosed()) { + channel.connection.sendChannelEOF(channel); + } + } + + /** + * + * + * @param b + * @param off + * @param len + * + * @throws IOException + */ + public void write(byte[] b, int off, int len) throws IOException { + if (isClosed) { + throw new IOException("The ChannelOutputStream is closed!"); + } + + byte[] data = null; + + if ((off > 0) || (len < b.length)) { + data = new byte[len]; + System.arraycopy(b, off, data, 0, len); + } else { + data = b; + } + + sendChannelData(data); + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void write(int b) throws IOException { + if (isClosed) { + throw new IOException("The ChannelOutputStream is closed!"); + } + + byte[] data = new byte[1]; + data[0] = (byte) b; + sendChannelData(data); + } + + private void sendChannelData(byte[] data) throws IOException { + channel.sendChannelData(data); + + /* int sent = 0; + int block; + int remaining; + long max; + byte[] buffer; + ChannelDataWindow window = channel.getRemoteWindow(); + while (sent < data.length) { + remaining = data.length - sent; + max = ((window.getWindowSpace() < channel.getRemotePacketSize()) + && window.getWindowSpace() > 0) + ? window.getWindowSpace() : channel.getRemotePacketSize(); + block = (max < remaining) ? (int) max : remaining; + channel.remoteWindow.consumeWindowSpace(block); + buffer = new byte[block]; + System.arraycopy(data, sent, buffer, 0, block); + if (type != null) { + channel.sendChannelExtData(type.intValue(), buffer); + } else { + channel.sendChannelData(buffer); + } + sent += block; + }*/ + } +} diff --git a/src/com/sshtools/j2ssh/connection/ChannelState.java b/src/com/sshtools/j2ssh/connection/ChannelState.java new file mode 100644 index 0000000000000000000000000000000000000000..1145ebefc4ecd3e6ac38b3290aff7fcbab2ac725 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ChannelState.java @@ -0,0 +1,65 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.util.State; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class ChannelState extends State { + /** */ + public final static int CHANNEL_UNINITIALIZED = 1; + + /** */ + public final static int CHANNEL_OPEN = 2; + + /** */ + public final static int CHANNEL_CLOSED = 3; + + /** + * Creates a new ChannelState object. + */ + public ChannelState() { + super(CHANNEL_UNINITIALIZED); + } + + /** + * + * + * @param state + * + * @return + */ + public boolean isValidState(int state) { + return ((state == CHANNEL_UNINITIALIZED) || (state == CHANNEL_OPEN) || + (state == CHANNEL_CLOSED)); + } +} diff --git a/src/com/sshtools/j2ssh/connection/ConnectionProtocol.java b/src/com/sshtools/j2ssh/connection/ConnectionProtocol.java new file mode 100644 index 0000000000000000000000000000000000000000..e7741687124530fbf3b7e86315ae344027d53d06 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/ConnectionProtocol.java @@ -0,0 +1,940 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.SshException; +import com.sshtools.j2ssh.transport.AsyncService; +import com.sshtools.j2ssh.transport.MessageStoreEOFException; +import com.sshtools.j2ssh.transport.ServiceState; +import com.sshtools.j2ssh.transport.SshMessage; +import com.sshtools.j2ssh.transport.TransportProtocolState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.68 $ + */ +public class ConnectionProtocol extends AsyncService { + private static Log log = LogFactory.getLog(ConnectionProtocol.class); + private HashSet reusableChannels = new HashSet(); + private Map activeChannels = new ConcurrentHashMap(); + private Map allowedChannels = new HashMap(); + private Map globalRequests = new HashMap(); + private long nextChannelId = 0; + + /** + * Creates a new ConnectionProtocol object. + */ + public ConnectionProtocol() { + super("ssh-connection"); + } + + /** + * + * + * @param channelName + * @param cf + * + * @throws IOException + */ + public void addChannelFactory(String channelName, ChannelFactory cf) + throws IOException { + allowedChannels.put(channelName, cf); + } + + /** + * + * + * @param channelName + */ + public void removeChannelFactory(String channelName) { + allowedChannels.remove(channelName); + } + + /** + * + * + * @param channelName + * + * @return + */ + public boolean containsChannelFactory(String channelName) { + return allowedChannels.containsKey(channelName); + } + + /** + * + * + * @param requestName + * @param handler + */ + public void allowGlobalRequest(String requestName, + GlobalRequestHandler handler) { + globalRequests.put(requestName, handler); + } + + /** + * + * + * @param channel + * + * @return + * + * @throws IOException + */ + public synchronized boolean openChannel(Channel channel) + throws IOException { + return openChannel(channel, null); + } + + /** + * + * + * @return + */ + public boolean isConnected() { + return ((transport.getState().getValue() == TransportProtocolState.CONNECTED) || + (transport.getState().getValue() == TransportProtocolState.PERFORMING_KEYEXCHANGE)) && + (getState().getValue() == ServiceState.SERVICE_STARTED); + } + + private Long getChannelId() { + // synchronized (activeChannels) { + if (reusableChannels.size() <= 0) { + return new Long(nextChannelId++); + } else { + return (Long) reusableChannels.iterator().next(); + } + //} + } + + /** + * + * + * @param channel + * @param eventListener + * + * @return + * + * @throws IOException + * @throws SshException + */ + public synchronized boolean openChannel(Channel channel, + ChannelEventListener eventListener) throws IOException { + //synchronized (activeChannels) { + Long channelId = getChannelId(); + + // Create the message + SshMsgChannelOpen msg = new SshMsgChannelOpen(channel.getChannelType(), + channelId.longValue(), + channel.getLocalWindow().getWindowSpace(), + channel.getLocalPacketSize(), channel.getChannelOpenData()); + + // Send the message + transport.sendMessage(msg, this); + + // Wait for the next message to confirm the open channel (or not) + int[] messageIdFilter = new int[2]; + messageIdFilter[0] = SshMsgChannelOpenConfirmation.SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + messageIdFilter[1] = SshMsgChannelOpenFailure.SSH_MSG_CHANNEL_OPEN_FAILURE; + + try { + SshMessage result = messageStore.getMessage(messageIdFilter); + + if (result.getMessageId() == SshMsgChannelOpenConfirmation.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) { + SshMsgChannelOpenConfirmation conf = (SshMsgChannelOpenConfirmation) result; + activeChannels.put(channelId, channel); + log.debug("Initiating channel"); + channel.init(this, channelId.longValue(), + conf.getSenderChannel(), conf.getInitialWindowSize(), + conf.getMaximumPacketSize(), eventListener); + channel.open(); + log.info("Channel " + + String.valueOf(channel.getLocalChannelId()) + + " is open [" + channel.getName() + "]"); + + return true; + } else { + // Make sure the channels state is closed + channel.getState().setValue(ChannelState.CHANNEL_CLOSED); + + return false; + } + } catch (MessageStoreEOFException mse) { + throw new IOException(mse.getMessage()); + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for a connection protocol message"); + } + //} + } + + /** + * + */ + protected synchronized void onStop() { + log.info("Closing all active channels"); + // synchronized (activeChannels) { + log.info("thread has "+activeChannels.values().size()+" active channels to stop"); + try { + Channel channel; + + for (Iterator x = activeChannels.values().iterator(); x.hasNext();) { + channel = (Channel) x.next(); + + if (channel != null) { + if (log.isDebugEnabled()) { + log.debug("Closing " + channel.getName() + " id=" + + String.valueOf(channel.getLocalChannelId())); + } + + channel.close(); + } + } + } catch (Throwable t) { + log.error("Unable to close all channels: "+t.getMessage(),t); + } + + activeChannels.clear(); + // } + } + + /** + * + * + * @param channel + * @param data + * + * @throws IOException + */ + public synchronized void sendChannelData(Channel channel, byte[] data) + throws IOException { + synchronized (channel.getState()) { + if (log.isDebugEnabled()) { + log.debug("Sending " + String.valueOf(data.length) + + " bytes for channel id " + + String.valueOf(channel.getLocalChannelId())); + } + + int sent = 0; + int block; + int remaining; + long max; + byte[] buffer; + ChannelDataWindow window = channel.getRemoteWindow(); + + while (sent < data.length) { + remaining = data.length - sent; + max = ((window.getWindowSpace() < channel.getRemotePacketSize()) && + (window.getWindowSpace() > 0)) ? window.getWindowSpace() + : channel.getRemotePacketSize(); + block = (max < remaining) ? (int) max : remaining; + channel.remoteWindow.consumeWindowSpace(block); + buffer = new byte[block]; + System.arraycopy(data, sent, buffer, 0, block); + + SshMsgChannelData msg = new SshMsgChannelData(channel.getRemoteChannelId(), + buffer); + transport.sendMessage(msg, this); + + /* if (type != null) { + channel.sendChannelExtData(type.intValue(), buffer); + } else { + channel.sendChannelData(buffer); + }*/ + sent += block; + } + } + } + + /** + * + * + * @param channel + * + * @throws IOException + */ + public void sendChannelEOF(Channel channel) throws IOException { + //synchronized (activeChannels) { + if (!activeChannels.containsValue(channel)) { + throw new IOException( + "Attempt to send EOF for a non existent channel " + + String.valueOf(channel.getLocalChannelId())); + } + + log.info("Local computer has set channel " + + String.valueOf(channel.getLocalChannelId()) + " to EOF [" + + channel.getName() + "]"); + + SshMsgChannelEOF msg = new SshMsgChannelEOF(channel.getRemoteChannelId()); + transport.sendMessage(msg, this); + // } + } + + /** + * + * + * @param channel + * @param extendedType + * @param data + * + * @throws IOException + */ + public synchronized void sendChannelExtData(Channel channel, + int extendedType, byte[] data) throws IOException { + channel.getRemoteWindow().consumeWindowSpace(data.length); + + int sent = 0; + int block; + int remaining; + long max; + byte[] buffer; + ChannelDataWindow window = channel.getRemoteWindow(); + + while (sent < data.length) { + remaining = data.length - sent; + max = ((window.getWindowSpace() < channel.getRemotePacketSize()) && + (window.getWindowSpace() > 0)) ? window.getWindowSpace() + : channel.getRemotePacketSize(); + block = (max < remaining) ? (int) max : remaining; + channel.remoteWindow.consumeWindowSpace(block); + buffer = new byte[block]; + System.arraycopy(data, sent, buffer, 0, block); + + SshMsgChannelExtendedData msg = new SshMsgChannelExtendedData(channel.getRemoteChannelId(), + extendedType, buffer); + transport.sendMessage(msg, this); + + /* if (type != null) { + channel.sendChannelExtData(type.intValue(), buffer); + } else { + channel.sendChannelData(buffer); + }*/ + sent += block; + } + } + + /** + * + * + * @param channel + * @param requestType + * @param wantReply + * @param requestData + * + * @return + * + * @throws IOException + * @throws SshException + */ + public synchronized boolean sendChannelRequest(Channel channel, + String requestType, boolean wantReply, byte[] requestData) + throws IOException { + boolean success = true; + log.info("Sending " + requestType + " request for the " + + channel.getChannelType() + " channel"); + + SshMsgChannelRequest msg = new SshMsgChannelRequest(channel.getRemoteChannelId(), + requestType, wantReply, requestData); + transport.sendMessage(msg, this); + + // If the user requests a reply then wait for the message and return result + if (wantReply) { + // Set up our message filter + int[] messageIdFilter = new int[2]; + + messageIdFilter[0] = SshMsgChannelSuccess.SSH_MSG_CHANNEL_SUCCESS; + messageIdFilter[1] = SshMsgChannelFailure.SSH_MSG_CHANNEL_FAILURE; + + log.info("Waiting for channel request reply"); + + try { + // Wait for either success or failure + SshMessage reply = messageStore.getMessage(messageIdFilter); + + switch (reply.getMessageId()) { + case SshMsgChannelSuccess.SSH_MSG_CHANNEL_SUCCESS: { + log.info("Channel request succeeded"); + success = true; + + break; + } + + case SshMsgChannelFailure.SSH_MSG_CHANNEL_FAILURE: { + log.info("Channel request failed"); + success = false; + + break; + } + } + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for a connection protocol message"); + } + } + + return success; + } + + /** + * + * + * @param channel + * + * @throws IOException + */ + public void sendChannelRequestFailure(Channel channel) + throws IOException { + SshMsgChannelFailure msg = new SshMsgChannelFailure(channel.getRemoteChannelId()); + transport.sendMessage(msg, this); + } + + /** + * + * + * @param channel + * + * @throws IOException + */ + public void sendChannelRequestSuccess(Channel channel) + throws IOException { + SshMsgChannelSuccess msg = new SshMsgChannelSuccess(channel.getRemoteChannelId()); + transport.sendMessage(msg, this); + } + + /** + * + * + * @param channel + * @param bytesToAdd + * + * @throws IOException + */ + public void sendChannelWindowAdjust(Channel channel, long bytesToAdd) + throws IOException { + log.debug("Increasing window size by " + String.valueOf(bytesToAdd) + + " bytes"); + + SshMsgChannelWindowAdjust msg = new SshMsgChannelWindowAdjust(channel.getRemoteChannelId(), + bytesToAdd); + transport.sendMessage(msg, this); + } + + /** + * + * + * @param requestName + * @param wantReply + * @param requestData + * + * @return + * + * @throws IOException + * @throws SshException + */ + public synchronized byte[] sendGlobalRequest(String requestName, + boolean wantReply, byte[] requestData) throws IOException { + boolean success = true; + SshMsgGlobalRequest msg = new SshMsgGlobalRequest(requestName, true, + requestData); + transport.sendMessage(msg, this); + + if (wantReply) { + // Set up our message filter + int[] messageIdFilter = new int[2]; + messageIdFilter[0] = SshMsgRequestSuccess.SSH_MSG_REQUEST_SUCCESS; + messageIdFilter[1] = SshMsgRequestFailure.SSH_MSG_REQUEST_FAILURE; + log.debug("Waiting for global request reply"); + + try { + // Wait for either success or failure + SshMessage reply = messageStore.getMessage(messageIdFilter); + + switch (reply.getMessageId()) { + case SshMsgRequestSuccess.SSH_MSG_REQUEST_SUCCESS: { + log.debug("Global request succeeded"); + + return ((SshMsgRequestSuccess) reply).getRequestData(); + } + + case SshMsgRequestFailure.SSH_MSG_REQUEST_FAILURE: { + log.debug("Global request failed"); + throw new SshException("The request failed"); + } + } + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for a connection protocol message"); + } + } + + return null; + } + + /** + * + * + * @return + */ + protected int[] getAsyncMessageFilter() { + int[] messageFilter = new int[10]; + messageFilter[0] = SshMsgGlobalRequest.SSH_MSG_GLOBAL_REQUEST; + messageFilter[3] = SshMsgChannelOpen.SSH_MSG_CHANNEL_OPEN; + messageFilter[4] = SshMsgChannelClose.SSH_MSG_CHANNEL_CLOSE; + messageFilter[5] = SshMsgChannelEOF.SSH_MSG_CHANNEL_EOF; + messageFilter[6] = SshMsgChannelExtendedData.SSH_MSG_CHANNEL_EXTENDED_DATA; + messageFilter[7] = SshMsgChannelData.SSH_MSG_CHANNEL_DATA; + messageFilter[8] = SshMsgChannelRequest.SSH_MSG_CHANNEL_REQUEST; + messageFilter[9] = SshMsgChannelWindowAdjust.SSH_MSG_CHANNEL_WINDOW_ADJUST; + + return messageFilter; + } + + /** + * + * + * @param channel + * + * @throws IOException + */ + protected void closeChannel(Channel channel) throws IOException { + SshMsgChannelClose msg = new SshMsgChannelClose(channel.getRemoteChannelId()); + log.info("Local computer has closed channel " + + String.valueOf(channel.getLocalChannelId()) + "[" + + channel.getName() + "]"); + transport.sendMessage(msg, this); + } + + /** + * + * + * @param requestName + * @param wantReply + * @param requestData + * + * @throws IOException + */ + protected void onGlobalRequest(String requestName, boolean wantReply, + byte[] requestData) throws IOException { + log.debug("Processing " + requestName + " global request"); + + if (!globalRequests.containsKey(requestName)) { + sendGlobalRequestFailure(); + } else { + GlobalRequestHandler handler = (GlobalRequestHandler) globalRequests.get(requestName); + GlobalRequestResponse response = handler.processGlobalRequest(requestName, + requestData); + + if (wantReply) { + if (response.hasSucceeded()) { + sendGlobalRequestSuccess(response.getResponseData()); + } else { + sendGlobalRequestFailure(); + } + } + } + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void onMessageReceived(SshMessage msg) throws IOException { + // Route the message to the correct handling function + switch (msg.getMessageId()) { + case SshMsgGlobalRequest.SSH_MSG_GLOBAL_REQUEST: { + onMsgGlobalRequest((SshMsgGlobalRequest) msg); + + break; + } + + case SshMsgChannelOpen.SSH_MSG_CHANNEL_OPEN: { + onMsgChannelOpen((SshMsgChannelOpen) msg); + + break; + } + + case SshMsgChannelClose.SSH_MSG_CHANNEL_CLOSE: { + onMsgChannelClose((SshMsgChannelClose) msg); + + break; + } + + case SshMsgChannelEOF.SSH_MSG_CHANNEL_EOF: { + onMsgChannelEOF((SshMsgChannelEOF) msg); + + break; + } + + case SshMsgChannelData.SSH_MSG_CHANNEL_DATA: { + onMsgChannelData((SshMsgChannelData) msg); + + break; + } + + case SshMsgChannelExtendedData.SSH_MSG_CHANNEL_EXTENDED_DATA: { + onMsgChannelExtendedData((SshMsgChannelExtendedData) msg); + + break; + } + + case SshMsgChannelRequest.SSH_MSG_CHANNEL_REQUEST: { + onMsgChannelRequest((SshMsgChannelRequest) msg); + + break; + } + + case SshMsgChannelWindowAdjust.SSH_MSG_CHANNEL_WINDOW_ADJUST: { + onMsgChannelWindowAdjust((SshMsgChannelWindowAdjust) msg); + + break; + } + + default: { + // If we never registered it why are we getting it? + log.debug("Message not handled"); + throw new IOException("Unregistered message received!"); + } + } + } + + /** + * + */ + protected void onServiceAccept() { + } + + /** + * + * + * @param startMode + * + * @throws IOException + */ + protected void onServiceInit(int startMode) throws IOException { + log.info("Registering connection protocol messages"); + messageStore.registerMessage(SshMsgChannelOpenConfirmation.SSH_MSG_CHANNEL_OPEN_CONFIRMATION, + SshMsgChannelOpenConfirmation.class); + messageStore.registerMessage(SshMsgChannelOpenFailure.SSH_MSG_CHANNEL_OPEN_FAILURE, + SshMsgChannelOpenFailure.class); + messageStore.registerMessage(SshMsgChannelOpen.SSH_MSG_CHANNEL_OPEN, + SshMsgChannelOpen.class); + messageStore.registerMessage(SshMsgChannelClose.SSH_MSG_CHANNEL_CLOSE, + SshMsgChannelClose.class); + messageStore.registerMessage(SshMsgChannelEOF.SSH_MSG_CHANNEL_EOF, + SshMsgChannelEOF.class); + messageStore.registerMessage(SshMsgChannelData.SSH_MSG_CHANNEL_DATA, + SshMsgChannelData.class); + messageStore.registerMessage(SshMsgChannelExtendedData.SSH_MSG_CHANNEL_EXTENDED_DATA, + SshMsgChannelExtendedData.class); + messageStore.registerMessage(SshMsgChannelFailure.SSH_MSG_CHANNEL_FAILURE, + SshMsgChannelFailure.class); + messageStore.registerMessage(SshMsgChannelRequest.SSH_MSG_CHANNEL_REQUEST, + SshMsgChannelRequest.class); + messageStore.registerMessage(SshMsgChannelSuccess.SSH_MSG_CHANNEL_SUCCESS, + SshMsgChannelSuccess.class); + messageStore.registerMessage(SshMsgChannelWindowAdjust.SSH_MSG_CHANNEL_WINDOW_ADJUST, + SshMsgChannelWindowAdjust.class); + messageStore.registerMessage(SshMsgGlobalRequest.SSH_MSG_GLOBAL_REQUEST, + SshMsgGlobalRequest.class); + messageStore.registerMessage(SshMsgRequestFailure.SSH_MSG_REQUEST_FAILURE, + SshMsgRequestFailure.class); + messageStore.registerMessage(SshMsgRequestSuccess.SSH_MSG_REQUEST_SUCCESS, + SshMsgRequestSuccess.class); + } + + /** + * + */ + protected void onServiceRequest() { + } + + /** + * + * + * @param channel + * + * @throws IOException + */ + protected void sendChannelFailure(Channel channel) + throws IOException { + SshMsgChannelFailure msg = new SshMsgChannelFailure(channel.getRemoteChannelId()); + transport.sendMessage(msg, this); + } + + /** + * + * + * @param channel + * + * @throws IOException + */ + protected void sendChannelOpenConfirmation(Channel channel) + throws IOException { + SshMsgChannelOpenConfirmation msg = new SshMsgChannelOpenConfirmation(channel.getRemoteChannelId(), + channel.getLocalChannelId(), + channel.getLocalWindow().getWindowSpace(), + channel.getLocalPacketSize(), + channel.getChannelConfirmationData()); + transport.sendMessage(msg, this); + } + + /** + * + * + * @param remoteChannelId + * @param reasonCode + * @param additionalInfo + * @param languageTag + * + * @throws IOException + */ + protected void sendChannelOpenFailure(long remoteChannelId, + long reasonCode, String additionalInfo, String languageTag) + throws IOException { + SshMsgChannelOpenFailure msg = new SshMsgChannelOpenFailure(remoteChannelId, + reasonCode, additionalInfo, languageTag); + transport.sendMessage(msg, this); + } + + /** + * + * + * @throws IOException + */ + protected void sendGlobalRequestFailure() throws IOException { + SshMsgRequestFailure msg = new SshMsgRequestFailure(); + transport.sendMessage(msg, this); + } + + /** + * + * + * @param requestData + * + * @throws IOException + */ + protected void sendGlobalRequestSuccess(byte[] requestData) + throws IOException { + SshMsgRequestSuccess msg = new SshMsgRequestSuccess(requestData); + transport.sendMessage(msg, this); + } + + private Channel getChannel(long channelId) throws IOException { + //synchronized (activeChannels) { + Long l = new Long(channelId); + + if (!activeChannels.containsKey(l)) { + throw new IOException("Non existent channel " + l.toString() + + " requested"); + } + return (Channel) activeChannels.get(l); + //} + } + + private void onMsgChannelClose(SshMsgChannelClose msg) + throws IOException { + Channel channel = getChannel(msg.getRecipientChannel()); + + // If we have not already closed it then inform the subclasses + if (channel == null) { + throw new IOException("Remote computer tried to close a " + + "non existent channel " + + String.valueOf(msg.getRecipientChannel())); + } + + log.info("Remote computer has closed channel " + + String.valueOf(channel.getLocalChannelId()) + "[" + + channel.getName() + "]"); + + // If the channel is not already closed then close it + if (channel.getState().getValue() != ChannelState.CHANNEL_CLOSED) { + channel.remoteClose(); + } + } + + private void onMsgChannelData(SshMsgChannelData msg) + throws IOException { + if (log.isDebugEnabled()) { + log.debug("Received " + + String.valueOf(msg.getChannelData().length) + + " bytes of data for channel id " + + String.valueOf(msg.getRecipientChannel())); + } + + // Get the data's channel + Channel channel = getChannel(msg.getRecipientChannel()); + channel.processChannelData(msg); + } + + private void onMsgChannelEOF(SshMsgChannelEOF msg) + throws IOException { + Channel channel = getChannel(msg.getRecipientChannel()); + + try { + log.info("Remote computer has set channel " + + String.valueOf(msg.getRecipientChannel()) + " to EOF [" + + channel.getName() + "]"); + channel.setRemoteEOF(); + } catch (IOException ioe) { + log.info("Failed to close the ChannelInputStream after EOF event"); + } + } + + private void onMsgChannelExtendedData(SshMsgChannelExtendedData msg) + throws IOException { + Channel channel = getChannel(msg.getRecipientChannel()); + + if (channel == null) { + throw new IOException( + "Remote computer sent data for non existent channel"); + } + + channel.getLocalWindow().consumeWindowSpace(msg.getChannelData().length); + channel.processChannelData(msg); + } + + private void onMsgChannelOpen(SshMsgChannelOpen msg) + throws IOException { + //synchronized (activeChannels) { + log.info("Request for " + msg.getChannelType() + + " channel recieved"); + + // Try to get the channel implementation from the allowed channels + ChannelFactory cf = (ChannelFactory) allowedChannels.get(msg.getChannelType()); + + if (cf == null) { + sendChannelOpenFailure(msg.getSenderChannelId(), + SshMsgChannelOpenFailure.SSH_OPEN_CONNECT_FAILED, + "The channel type is not supported", ""); + log.info("Request for channel type " + msg.getChannelType() + + " refused"); + + return; + } + + try { + log.info("Creating channel " + msg.getChannelType()); + + Channel channel = cf.createChannel(msg.getChannelType(), + msg.getChannelData()); + + // Initialize the channel + log.info("Initiating channel"); + + Long channelId = getChannelId(); + channel.init(this, channelId.longValue(), + msg.getSenderChannelId(), msg.getInitialWindowSize(), + msg.getMaximumPacketSize()); + activeChannels.put(channelId, channel); + log.info("Sending channel open confirmation"); + + // Send the confirmation message + sendChannelOpenConfirmation(channel); + + // Open the channel for real + channel.open(); + } catch (InvalidChannelException ice) { + sendChannelOpenFailure(msg.getSenderChannelId(), + SshMsgChannelOpenFailure.SSH_OPEN_CONNECT_FAILED, + ice.getMessage(), ""); + } + //} + } + + private void onMsgChannelRequest(SshMsgChannelRequest msg) + throws IOException { + Channel channel = getChannel(msg.getRecipientChannel()); + + if (channel == null) { + log.warn("Remote computer tried to make a request for " + + "a non existence channel!"); + } + + channel.onChannelRequest(msg.getRequestType(), msg.getWantReply(), + msg.getChannelData()); + } + + private void onMsgChannelWindowAdjust(SshMsgChannelWindowAdjust msg) + throws IOException { + Channel channel = getChannel(msg.getRecipientChannel()); + + if (channel == null) { + throw new IOException("Remote computer tried to increase " + + "window space for non existent channel " + + String.valueOf(msg.getRecipientChannel())); + } + + channel.getRemoteWindow().increaseWindowSpace(msg.getBytesToAdd()); + + if (log.isDebugEnabled()) { + log.debug(String.valueOf(msg.getBytesToAdd()) + + " bytes added to remote window"); + log.debug("Remote window space is " + + String.valueOf(channel.getRemoteWindow().getWindowSpace())); + } + } + + private void onMsgGlobalRequest(SshMsgGlobalRequest msg) + throws IOException { + onGlobalRequest(msg.getRequestName(), msg.getWantReply(), + msg.getRequestData()); + } + + /** + * + * + * @param channel + */ + protected void freeChannel(Channel channel) { + //synchronized (activeChannels) { + log.info("Freeing channel " + + String.valueOf(channel.getLocalChannelId()) + " [" + + channel.getName() + "]"); + + Long channelId = new Long(channel.getLocalChannelId()); + activeChannels.remove(channelId); + + //reusableChannels.add(channelId); + //} + } +} diff --git a/src/com/sshtools/j2ssh/connection/GlobalRequestHandler.java b/src/com/sshtools/j2ssh/connection/GlobalRequestHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..d4c5afdb43efe537d2a25894d8d1dea167b9f183 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/GlobalRequestHandler.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public interface GlobalRequestHandler { + /** + * + * + * @param requestName + * @param requestData + * + * @return + */ + public GlobalRequestResponse processGlobalRequest(String requestName, + byte[] requestData); +} diff --git a/src/com/sshtools/j2ssh/connection/GlobalRequestResponse.java b/src/com/sshtools/j2ssh/connection/GlobalRequestResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..26c50c512a1874fea19c7670f66e361f6d0608d2 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/GlobalRequestResponse.java @@ -0,0 +1,76 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class GlobalRequestResponse { + //public static final GlobalRequestResponse REQUEST_FAILED = new GlobalRequestResponse(false); + //public static final GlobalRequestResponse REQUEST_SUCCEEDED = new GlobalRequestResponse(true); + private byte[] responseData = null; + private boolean succeeded; + + /** + * Creates a new GlobalRequestResponse object. + * + * @param succeeded + */ + public GlobalRequestResponse(boolean succeeded) { + this.succeeded = succeeded; + } + + /** + * + * + * @param responseData + */ + public void setResponseData(byte[] responseData) { + this.responseData = responseData; + } + + /** + * + * + * @return + */ + public byte[] getResponseData() { + return responseData; + } + + /** + * + * + * @return + */ + public boolean hasSucceeded() { + return succeeded; + } +} diff --git a/src/com/sshtools/j2ssh/connection/IOChannel.java b/src/com/sshtools/j2ssh/connection/IOChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..8275b3e4ef03fa16aed8c61e228153097c44b951 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/IOChannel.java @@ -0,0 +1,321 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.IOStreamConnector; +import com.sshtools.j2ssh.transport.MessageNotAvailableException; +import com.sshtools.j2ssh.transport.MessageStoreEOFException; +import com.sshtools.j2ssh.transport.SshMessageStore; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public abstract class IOChannel extends Channel { + private static Log log = LogFactory.getLog(IOChannel.class); + + /** */ + private SshMessageStore incoming = new SshMessageStore(); + + /** */ + protected ChannelInputStream in; + + /** */ + protected ChannelOutputStream out; + + /** */ + protected InputStream boundInputStream = null; + + /** */ + protected OutputStream boundOutputStream = null; + + //protected IOChannel boundIOChannel = null; + + /** */ + protected IOStreamConnector ios = null; + + /** + * + * + * @param connection + * @param localChannelId + * @param senderChannelId + * @param initialWindowSize + * @param maximumPacketSize + * + * @throws IOException + */ + protected void init(ConnectionProtocol connection, long localChannelId, + long senderChannelId, long initialWindowSize, long maximumPacketSize) + throws IOException { + this.in = new ChannelInputStream(incoming); //ChannelInputStream.createStandard(incoming); + this.out = new ChannelOutputStream(this); + super.init(connection, localChannelId, senderChannelId, + initialWindowSize, maximumPacketSize); + } + + /** + * + * + * @throws IOException + */ + protected void open() throws IOException { + super.open(); + + // If were bound send any outstanding messages sitting around + if (boundOutputStream != null) { + sendOutstandingMessages(); + } + + // Start the bound inputstream + if ((boundInputStream != null) && (ios == null)) { + ios.setCloseInput(false); + ios.setCloseOutput(false); + ios.connect(boundInputStream, out); + } + } + + /** + * + * + * @return + */ + public ChannelInputStream getInputStream() { + return in; + } + + /** + * + * + * @return + */ + public ChannelOutputStream getOutputStream() { + return out; + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void onChannelData(SshMsgChannelData msg) + throws IOException { + // Synchronize on the message store to ensure that another thread + // does not try to read its data. This will make sure that the incoming + // messages are not being flushed to an outputstream after a bind + synchronized (incoming) { + if (boundOutputStream != null) { + try { + boundOutputStream.write(msg.getChannelData()); + } catch (IOException ex) { + log.info( + "Could not route data to the bound OutputStream; Closing channel."); + log.info(ex.getMessage()); + close(); + } + } else { + incoming.addMessage(msg); + } + } + } + + /** + * + * + * @throws IOException + */ + public void setLocalEOF() throws IOException { + super.setLocalEOF(); + + if (!out.isClosed()) { + out.close(); + } + } + + /** + * + * + * @throws IOException + */ + protected void onChannelEOF() throws IOException { + if (!in.isClosed()) { + in.close(); + } + } + + /** + * + * + * @throws IOException + */ + protected void onChannelClose() throws IOException { + // Close the input/output streams + if (!in.isClosed()) { + in.close(); + } + + if (!out.isClosed()) { + out.close(); + } + + // Close the bound channel + + /* if(boundIOChannel!=null && !boundIOChannel.isClosed()) + boundIOChannel.close();*/ + + // Close the IOStream connector if were bound + if (ios != null) { + ios.close(); + } + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void onChannelExtData(SshMsgChannelExtendedData msg) + throws IOException { + // This class will not deal with extended data + // incoming.addMessage(msg); + } + + /*public void bindIOChannel(IOChannel boundIOChannel) throws IOException { + this.boundIOChannel = boundIOChannel; + // If the bound channel is open then bind the outputstreams + if (boundIOChannel.getState().getValue() == ChannelState.CHANNEL_OPEN) { + throw new IOException("You cannot bind to an open channel"); + } + // Create an event listener so we can listen + boundIOChannel.addEventListener(new ChannelEventListener() { + public void onChannelOpen(Channel channel) { + try { + bindOutputStream(IOChannel.this.boundIOChannel.getOutputStream()); + IOChannel.this.boundIOChannel.bindOutputStream(getOutputStream()); + } + catch (IOException ex) { + log.info("Failed to bind the channel"); + } + } + public void onChannelEOF(Channel channel) { + try { + //setLocalEOF(); + close(); + } + catch (IOException ex) { + log.info("Failed to set the channel to EOF"); + } + } + public void onChannelClose(Channel channel) { + try { + if(!isClosed()) + close(); + } + catch (IOException ex) { + log.info("Failed to close the channel"); + } + } + public void onDataReceived(Channel channel, byte[] data) { + } + public void onDataSent(Channel channel, byte[] data) { + } + }); + }*/ + public void bindOutputStream(OutputStream boundOutputStream) + throws IOException { + // Synchronize on the incoming message store to ensure that no other + // messages are added whilst we transfer to a bound state + synchronized (incoming) { + this.boundOutputStream = boundOutputStream; + + if (state.getValue() == ChannelState.CHANNEL_OPEN) { + sendOutstandingMessages(); + } + } + } + + /** + * + * + * @param boundInputStream + * + * @throws IOException + */ + public void bindInputStream(InputStream boundInputStream) + throws IOException { + this.boundInputStream = boundInputStream; + this.ios = new IOStreamConnector(); + + if (state.getValue() == ChannelState.CHANNEL_OPEN) { + ios.setCloseInput(false); + ios.setCloseOutput(false); + ios.connect(boundInputStream, out); + } + } + + private void sendOutstandingMessages() throws IOException { + if ((boundInputStream != null) && (boundOutputStream != null) && + incoming.hasMessages()) { + while (true) { + try { + // Peek into the message store and look for the next message + SshMsgChannelData msg = (SshMsgChannelData) incoming.peekMessage(SshMsgChannelData.SSH_MSG_CHANNEL_DATA); + + // Remove the message so we dont process again + incoming.removeMessage(msg); + + // Write the message out to the bound OutputStream + try { + boundOutputStream.write(msg.getChannelData()); + } catch (IOException ex1) { + //log.info("Could not write outstanding messages to the bound OutputStream: " +ex1.getMessage()); + close(); + } + } catch (MessageStoreEOFException ex) { + break; + } catch (MessageNotAvailableException ex) { + break; + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/InvalidChannelException.java b/src/com/sshtools/j2ssh/connection/InvalidChannelException.java new file mode 100644 index 0000000000000000000000000000000000000000..9158d4ec702ed8c426dd6e40598f479da9e09d2a --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/InvalidChannelException.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class InvalidChannelException extends IOException { + /** + * Creates a new InvalidChannelException object. + * + * @param msg + */ + public InvalidChannelException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/connection/SocketChannel.java b/src/com/sshtools/j2ssh/connection/SocketChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..478c7bdbccc4a3cc8b1a82dec2f8613f7fe7ccb2 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SocketChannel.java @@ -0,0 +1,199 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import java.net.Socket; +import java.net.SocketException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public abstract class SocketChannel extends Channel { + private static Log log = LogFactory.getLog(SocketChannel.class); + + /** */ + protected Socket socket = null; + Thread thread; + + /** + * + * + * @param socket + * + * @throws IOException + */ + public void bindSocket(Socket socket) throws IOException { + if (state.getValue() == ChannelState.CHANNEL_UNINITIALIZED) { + this.socket = socket; + } else { + throw new IOException( + "The socket can only be bound to an unitialized channel"); + } + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void onChannelData(SshMsgChannelData msg) + throws IOException { + try { + socket.getOutputStream().write(msg.getChannelData()); + } catch (IOException ex) { + } + } + + /** + * + * + * @throws IOException + */ + protected void onChannelEOF() throws IOException { + try { + //synchronized(state) { + //if (isOpen()) + // close(); + socket.shutdownOutput(); + + // } + } catch (IOException ex) { + log.info( + "Failed to shutdown Socket OutputStream in response to EOF event: " + + ex.getMessage()); + } + } + + /** + * + * + * @throws IOException + */ + protected void onChannelClose() throws IOException { + try { + socket.close(); + } catch (IOException ex) { + log.info("Failed to close socket on channel close event: " + + ex.getMessage()); + } + } + + /** + * + * + * @throws IOException + */ + protected void onChannelOpen() throws IOException { + if (socket == null) { + throw new IOException( + "The socket must be bound to the channel before opening"); + } + + thread = new Thread(new SocketReader()); + thread.start(); + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void onChannelExtData(SshMsgChannelExtendedData msg) + throws IOException { + // We do not have an extended data channel for the socket so ignore + } + + class SocketReader implements Runnable { + public void run() { + byte[] buffer = new byte[getMaximumPacketSize()]; + ByteArrayWriter baw = new ByteArrayWriter(); + + try { + socket.setSoTimeout(2000); + } catch (SocketException ex2) { + } + + try { + int read = 0; + + while ((read >= 0) && !isClosed()) { + try { + read = socket.getInputStream().read(buffer); + } catch (InterruptedIOException ex1) { + read = ex1.bytesTransferred; + } + + synchronized (state) { + if (isClosed() || isLocalEOF()) { + break; + } + + if (read > 0) { + baw.write(buffer, 0, read); + sendChannelData(baw.toByteArray()); + baw.reset(); + } + } + } + } catch (IOException ex) { + // Break out of the while loop + } + + try { + synchronized (state) { + if (!isLocalEOF()) { + setLocalEOF(); + } + + if (isOpen()) { + close(); + } + } + } catch (Exception ex) { + log.info("Failed to send channel EOF message: " + + ex.getMessage()); + } + + thread = null; + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelClose.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelClose.java new file mode 100644 index 0000000000000000000000000000000000000000..b31204e40b21ba0592d9dc5da286d0bafc9cf246 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelClose.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelClose extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_CLOSE = 97; + + // The recipient channel id + private long recipientChannel; + + /** + * Creates a new SshMsgChannelClose object. + * + * @param recipientChannel + */ + public SshMsgChannelClose(long recipientChannel) { + super(SSH_MSG_CHANNEL_CLOSE); + this.recipientChannel = recipientChannel; + } + + /** + * Creates a new SshMsgChannelClose object. + */ + public SshMsgChannelClose() { + super(SSH_MSG_CHANNEL_CLOSE); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_CLOSE"; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelData.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelData.java new file mode 100644 index 0000000000000000000000000000000000000000..4b773d50bcfe6127afcaaa7020c64e253a2f555e --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelData.java @@ -0,0 +1,148 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class SshMsgChannelData extends SshMessage { + /** */ + public final static int SSH_MSG_CHANNEL_DATA = 94; + + // The channel data + private byte[] channelData; + + // The recipient channel id + private long recipientChannel; + + /** + * Creates a new SshMsgChannelData object. + * + * @param recipientChannel + * @param channelData + */ + public SshMsgChannelData(long recipientChannel, byte[] channelData) { + super(SSH_MSG_CHANNEL_DATA); + this.recipientChannel = recipientChannel; + this.channelData = channelData; + } + + /** + * Creates a new SshMsgChannelData object. + */ + public SshMsgChannelData() { + super(SSH_MSG_CHANNEL_DATA); + } + + /** + * + * + * @return + */ + public byte[] getChannelData() { + return channelData; + } + + /** + * + * + * @return + */ + public long getChannelDataLength() { + return channelData.length; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_DATA"; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + + if (channelData != null) { + baw.writeBinaryString(channelData); + } else { + baw.writeInt(0); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + + if (bar.available() > 0) { + channelData = bar.readBinaryString(); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelEOF.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelEOF.java new file mode 100644 index 0000000000000000000000000000000000000000..af7dc33688ef78e9a44f0db4806a7e5734816b65 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelEOF.java @@ -0,0 +1,113 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelEOF extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_EOF = 96; + private long recipientChannel; + + /** + * Creates a new SshMsgChannelEOF object. + * + * @param recipientChannel + */ + public SshMsgChannelEOF(long recipientChannel) { + super(SSH_MSG_CHANNEL_EOF); + this.recipientChannel = recipientChannel; + } + + /** + * Creates a new SshMsgChannelEOF object. + */ + public SshMsgChannelEOF() { + super(SSH_MSG_CHANNEL_EOF); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_EOF"; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelExtendedData.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelExtendedData.java new file mode 100644 index 0000000000000000000000000000000000000000..7937aa5ee28aff12ea1f69e7f595d117631c137e --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelExtendedData.java @@ -0,0 +1,153 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class SshMsgChannelExtendedData extends SshMessage { + /** */ + public final static int SSH_MSG_CHANNEL_EXTENDED_DATA = 95; + + /** */ + public final static int SSH_EXTENDED_DATA_STDERR = 1; + private byte[] channelData; + private int dataTypeCode; + private long recipientChannel; + + /** + * Creates a new SshMsgChannelExtendedData object. + * + * @param recipientChannel + * @param dataTypeCode + * @param channelData + */ + public SshMsgChannelExtendedData(long recipientChannel, int dataTypeCode, + byte[] channelData) { + super(SSH_MSG_CHANNEL_EXTENDED_DATA); + this.recipientChannel = recipientChannel; + this.dataTypeCode = dataTypeCode; + this.channelData = channelData; + } + + /** + * Creates a new SshMsgChannelExtendedData object. + */ + public SshMsgChannelExtendedData() { + super(SSH_MSG_CHANNEL_EXTENDED_DATA); + } + + /** + * + * + * @return + */ + public byte[] getChannelData() { + return channelData; + } + + /** + * + * + * @return + */ + public int getDataTypeCode() { + return dataTypeCode; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_EXTENDED_DATA"; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + baw.writeInt(dataTypeCode); + + if (channelData != null) { + baw.writeBinaryString(channelData); + } else { + baw.writeString(""); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + dataTypeCode = (int) bar.readInt(); + + if (bar.available() > 0) { + channelData = bar.readBinaryString(); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelFailure.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelFailure.java new file mode 100644 index 0000000000000000000000000000000000000000..6be63a69dc287584509d935d9506e348e595eb72 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelFailure.java @@ -0,0 +1,113 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelFailure extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_FAILURE = 100; + private long channelId; + + /** + * Creates a new SshMsgChannelFailure object. + * + * @param recipientChannelId + */ + public SshMsgChannelFailure(long recipientChannelId) { + super(SSH_MSG_CHANNEL_FAILURE); + channelId = recipientChannelId; + } + + /** + * Creates a new SshMsgChannelFailure object. + */ + public SshMsgChannelFailure() { + super(SSH_MSG_CHANNEL_FAILURE); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_FAILURE"; + } + + /** + * + * + * @return + */ + public long getRecipientChannelId() { + return channelId; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(channelId); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + channelId = bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelOpen.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelOpen.java new file mode 100644 index 0000000000000000000000000000000000000000..46d2e8b5212bf2bc5f42aa9a96a5d110197fc3f7 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelOpen.java @@ -0,0 +1,177 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelOpen extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_OPEN = 90; + private String channelType; + private byte[] channelData; + private long initialWindowSize; + private long maximumPacketSize; + private long senderChannelId; + + /** + * Creates a new SshMsgChannelOpen object. + * + * @param channelType + * @param senderChannelId + * @param initialWindowSize + * @param maximumPacketSize + * @param channelData + */ + public SshMsgChannelOpen(String channelType, long senderChannelId, + long initialWindowSize, long maximumPacketSize, byte[] channelData) { + super(SSH_MSG_CHANNEL_OPEN); + this.channelType = channelType; + this.senderChannelId = senderChannelId; + this.initialWindowSize = initialWindowSize; + this.maximumPacketSize = maximumPacketSize; + this.channelData = channelData; + } + + /** + * Creates a new SshMsgChannelOpen object. + */ + public SshMsgChannelOpen() { + super(SSH_MSG_CHANNEL_OPEN); + } + + /** + * + * + * @return + */ + public byte[] getChannelData() { + return channelData; + } + + /** + * + * + * @return + */ + public String getChannelType() { + return channelType; + } + + /** + * + * + * @return + */ + public long getInitialWindowSize() { + return initialWindowSize; + } + + /** + * + * + * @return + */ + public long getMaximumPacketSize() { + return maximumPacketSize; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_OPEN"; + } + + /** + * + * + * @return + */ + public long getSenderChannelId() { + return senderChannelId; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(channelType); + baw.writeInt(senderChannelId); + baw.writeInt(initialWindowSize); + baw.writeInt(maximumPacketSize); + + if (channelData != null) { + baw.write(channelData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Could not write message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + channelType = bar.readString(); + senderChannelId = bar.readInt(); + initialWindowSize = bar.readInt(); + maximumPacketSize = bar.readInt(); + + if (bar.available() > 0) { + channelData = new byte[bar.available()]; + bar.read(channelData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelOpenConfirmation.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelOpenConfirmation.java new file mode 100644 index 0000000000000000000000000000000000000000..690d386d45a6af53a71ba37430c9a2926037a084 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelOpenConfirmation.java @@ -0,0 +1,178 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelOpenConfirmation extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91; + private byte[] channelData; + private long initialWindowSize; + private long maximumPacketSize; + private long recipientChannel; + private long senderChannel; + + /** + * Creates a new SshMsgChannelOpenConfirmation object. + * + * @param recipientChannel + * @param senderChannel + * @param initialWindowSize + * @param maximumPacketSize + * @param channelData + */ + public SshMsgChannelOpenConfirmation(long recipientChannel, + long senderChannel, long initialWindowSize, long maximumPacketSize, + byte[] channelData) { + super(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + this.recipientChannel = recipientChannel; + this.senderChannel = senderChannel; + this.initialWindowSize = initialWindowSize; + this.maximumPacketSize = maximumPacketSize; + this.channelData = channelData; + } + + /** + * Creates a new SshMsgChannelOpenConfirmation object. + */ + public SshMsgChannelOpenConfirmation() { + super(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + } + + /** + * + * + * @return + */ + public byte[] getChannelData() { + return channelData; + } + + /** + * + * + * @return + */ + public long getInitialWindowSize() { + return initialWindowSize; + } + + /** + * + * + * @return + */ + public long getMaximumPacketSize() { + return maximumPacketSize; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_OPEN_CONFIRMATION"; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @return + */ + public long getSenderChannel() { + return senderChannel; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + baw.writeInt(senderChannel); + baw.writeInt(initialWindowSize); + baw.writeInt(maximumPacketSize); + + if (channelData != null) { + baw.write(channelData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + senderChannel = bar.readInt(); + initialWindowSize = bar.readInt(); + maximumPacketSize = bar.readInt(); + + if (bar.available() > 0) { + channelData = new byte[bar.available()]; + bar.read(channelData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelOpenFailure.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelOpenFailure.java new file mode 100644 index 0000000000000000000000000000000000000000..e613c46863aa79777b7bb2eb4d5a72b0223f2acd --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelOpenFailure.java @@ -0,0 +1,168 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelOpenFailure extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_OPEN_FAILURE = 92; + + /** */ + protected final static long SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1; + + /** */ + protected final static long SSH_OPEN_CONNECT_FAILED = 2; + + /** */ + protected final static long SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3; + + /** */ + protected final static long SSH_OPEN_RESOURCE_SHORTAGE = 4; + private String additional; + private String languageTag; + private long reasonCode; + private long recipientChannel; + + /** + * Creates a new SshMsgChannelOpenFailure object. + * + * @param recipientChannel + * @param reasonCode + * @param additional + * @param languageTag + */ + public SshMsgChannelOpenFailure(long recipientChannel, long reasonCode, + String additional, String languageTag) { + super(SSH_MSG_CHANNEL_OPEN_FAILURE); + this.recipientChannel = recipientChannel; + this.reasonCode = reasonCode; + this.additional = additional; + this.languageTag = languageTag; + } + + /** + * Creates a new SshMsgChannelOpenFailure object. + */ + public SshMsgChannelOpenFailure() { + super(SSH_MSG_CHANNEL_OPEN_FAILURE); + } + + /** + * + * + * @return + */ + public String getAdditionalText() { + return additional; + } + + /** + * + * + * @return + */ + public String getLanguageTag() { + return languageTag; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_OPEN_FAILURE"; + } + + /** + * + * + * @return + */ + public long getReasonCode() { + return reasonCode; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + baw.writeInt(reasonCode); + baw.writeString(additional); + baw.writeString(languageTag); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + reasonCode = bar.readInt(); + additional = bar.readString(); + languageTag = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelRequest.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..6587944d8fba40f62ae200c20624f0c7741a9a26 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelRequest.java @@ -0,0 +1,163 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelRequest extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_REQUEST = 98; + private String requestType; + private byte[] channelData; + private boolean wantReply; + private long recipientChannel; + + /** + * Creates a new SshMsgChannelRequest object. + * + * @param recipientChannel + * @param requestType + * @param wantReply + * @param channelData + */ + public SshMsgChannelRequest(long recipientChannel, String requestType, + boolean wantReply, byte[] channelData) { + super(SSH_MSG_CHANNEL_REQUEST); + this.recipientChannel = recipientChannel; + this.requestType = requestType; + this.wantReply = wantReply; + this.channelData = channelData; + } + + /** + * Creates a new SshMsgChannelRequest object. + */ + public SshMsgChannelRequest() { + super(SSH_MSG_CHANNEL_REQUEST); + } + + /** + * + * + * @return + */ + public byte[] getChannelData() { + return channelData; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_REQUEST"; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @return + */ + public String getRequestType() { + return requestType; + } + + /** + * + * + * @return + */ + public boolean getWantReply() { + return wantReply; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + baw.writeString(requestType); + baw.write((wantReply ? 1 : 0)); + + if (channelData != null) { + baw.write(channelData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + requestType = bar.readString(); + wantReply = ((bar.read() == 0) ? false : true); + + if (bar.available() > 0) { + channelData = new byte[bar.available()]; + bar.read(channelData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelSuccess.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelSuccess.java new file mode 100644 index 0000000000000000000000000000000000000000..98d9408c543048959b588011390e9ef804377499 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelSuccess.java @@ -0,0 +1,113 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelSuccess extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_SUCCESS = 99; + private long channelId; + + /** + * Creates a new SshMsgChannelSuccess object. + * + * @param recipientChannelId + */ + public SshMsgChannelSuccess(long recipientChannelId) { + super(SSH_MSG_CHANNEL_SUCCESS); + channelId = recipientChannelId; + } + + /** + * Creates a new SshMsgChannelSuccess object. + */ + public SshMsgChannelSuccess() { + super(SSH_MSG_CHANNEL_SUCCESS); + } + + /** + * + * + * @return + */ + public long getChannelId() { + return channelId; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_SUCCESS"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(channelId); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + channelId = bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgChannelWindowAdjust.java b/src/com/sshtools/j2ssh/connection/SshMsgChannelWindowAdjust.java new file mode 100644 index 0000000000000000000000000000000000000000..0b308a67ccda3076f5233e9c4d2047a4cce97ffb --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgChannelWindowAdjust.java @@ -0,0 +1,127 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgChannelWindowAdjust extends SshMessage { + /** */ + protected final static int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93; + private long bytesToAdd; + private long recipientChannel; + + /** + * Creates a new SshMsgChannelWindowAdjust object. + * + * @param recipientChannel + * @param bytesToAdd + */ + public SshMsgChannelWindowAdjust(long recipientChannel, long bytesToAdd) { + super(SSH_MSG_CHANNEL_WINDOW_ADJUST); + this.recipientChannel = recipientChannel; + this.bytesToAdd = bytesToAdd; + } + + /** + * Creates a new SshMsgChannelWindowAdjust object. + */ + public SshMsgChannelWindowAdjust() { + super(SSH_MSG_CHANNEL_WINDOW_ADJUST); + } + + /** + * + * + * @return + */ + public long getBytesToAdd() { + return bytesToAdd; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_CHANNEL_WINDOW_ADJUST"; + } + + /** + * + * + * @return + */ + public long getRecipientChannel() { + return recipientChannel; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(recipientChannel); + baw.writeInt(bytesToAdd); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + recipientChannel = bar.readInt(); + bytesToAdd = bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgGlobalRequest.java b/src/com/sshtools/j2ssh/connection/SshMsgGlobalRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..a60a71d593712108710d5ebd49e9680544f8b22f --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgGlobalRequest.java @@ -0,0 +1,149 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgGlobalRequest extends SshMessage { + /** */ + protected final static int SSH_MSG_GLOBAL_REQUEST = 80; + private String requestName; + private byte[] requestData; + private boolean wantReply; + + /** + * Creates a new SshMsgGlobalRequest object. + * + * @param requestName + * @param wantReply + * @param requestData + */ + public SshMsgGlobalRequest(String requestName, boolean wantReply, + byte[] requestData) { + super(SSH_MSG_GLOBAL_REQUEST); + this.requestName = requestName; + this.wantReply = wantReply; + this.requestData = requestData; + } + + /** + * Creates a new SshMsgGlobalRequest object. + */ + public SshMsgGlobalRequest() { + super(SSH_MSG_GLOBAL_REQUEST); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_GLOBAL_REQUEST"; + } + + /** + * + * + * @return + */ + public byte[] getRequestData() { + return requestData; + } + + /** + * + * + * @return + */ + public String getRequestName() { + return requestName; + } + + /** + * + * + * @return + */ + public boolean getWantReply() { + return wantReply; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(requestName); + baw.write((wantReply ? 1 : 0)); + + if (requestData != null) { + baw.write(requestData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + requestName = bar.readString(); + wantReply = ((bar.read() == 0) ? false : true); + + if (bar.available() > 0) { + requestData = new byte[bar.available()]; + bar.read(requestData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgRequestFailure.java b/src/com/sshtools/j2ssh/connection/SshMsgRequestFailure.java new file mode 100644 index 0000000000000000000000000000000000000000..6991df315a7e869f089590229d09f30ad9dee61f --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgRequestFailure.java @@ -0,0 +1,81 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgRequestFailure extends SshMessage { + /** */ + public final static int SSH_MSG_REQUEST_FAILURE = 82; + + /** + * Creates a new SshMsgRequestFailure object. + */ + public SshMsgRequestFailure() { + super(SSH_MSG_REQUEST_FAILURE); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_REQUEST_FAILURE"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + } +} diff --git a/src/com/sshtools/j2ssh/connection/SshMsgRequestSuccess.java b/src/com/sshtools/j2ssh/connection/SshMsgRequestSuccess.java new file mode 100644 index 0000000000000000000000000000000000000000..f49eef169e487ab8d1c868b1dca6c06f64b7bb38 --- /dev/null +++ b/src/com/sshtools/j2ssh/connection/SshMsgRequestSuccess.java @@ -0,0 +1,118 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.connection; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgRequestSuccess extends SshMessage { + /** */ + protected final static int SSH_MSG_REQUEST_SUCCESS = 81; + private byte[] requestData; + + /** + * Creates a new SshMsgRequestSuccess object. + * + * @param requestData + */ + public SshMsgRequestSuccess(byte[] requestData) { + super(SSH_MSG_REQUEST_SUCCESS); + this.requestData = requestData; + } + + /** + * Creates a new SshMsgRequestSuccess object. + */ + public SshMsgRequestSuccess() { + super(SSH_MSG_REQUEST_SUCCESS); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_REQUEST_SUCCESS"; + } + + /** + * + * + * @return + */ + public byte[] getRequestData() { + return requestData; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + if (requestData != null) { + baw.write(requestData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + if (bar.available() > 0) { + requestData = new byte[bar.available()]; + bar.read(requestData); + } + } catch (IOException ioe) { + throw new InvalidMessageException("Invalid message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingBindingChannel.java b/src/com/sshtools/j2ssh/forwarding/ForwardingBindingChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..170f385dd2aaf6ed3630eae49f66d1f800285f5c --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingBindingChannel.java @@ -0,0 +1,184 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import com.sshtools.j2ssh.connection.BindingChannel; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +//import java.net.InetSocketAddress; +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class ForwardingBindingChannel extends BindingChannel + implements ForwardingChannel { + private static Log log = LogFactory.getLog(ForwardingSocketChannel.class); + private ForwardingChannelImpl channel; + + /** + * Creates a new ForwardingBindingChannel object. + * + * @param forwardType + * @param hostToConnectOrBind + * @param portToConnectOrBind + * @param originatingHost + * @param originatingPort + * + * @throws ForwardingConfigurationException + */ + public ForwardingBindingChannel(String forwardType, String name, + + /*ForwardingConfiguration config,*/ + String hostToConnectOrBind, int portToConnectOrBind, + String originatingHost, int originatingPort) + throws ForwardingConfigurationException { + if (!forwardType.equals(LOCAL_FORWARDING_CHANNEL) && + !forwardType.equals(REMOTE_FORWARDING_CHANNEL) && + !forwardType.equals(X11_FORWARDING_CHANNEL)) { + throw new ForwardingConfigurationException( + "The forwarding type is invalid"); + } + + channel = new ForwardingChannelImpl(forwardType, name, + hostToConnectOrBind, portToConnectOrBind, originatingHost, + originatingPort); + } + + public String getName() { + return channel.getName(); + } + + /** + * + * + * @return + */ + public byte[] getChannelOpenData() { + return channel.getChannelOpenData(); + } + + /** + * + * + * @return + */ + public byte[] getChannelConfirmationData() { + return channel.getChannelConfirmationData(); + } + + /** + * + * + * @return + */ + public String getChannelType() { + return channel.getChannelType(); + } + + /** + * + * + * @return + */ + protected int getMinimumWindowSpace() { + return 32768; + } + + /** + * + * + * @return + */ + protected int getMaximumWindowSpace() { + return 131072; + } + + /** + * + * + * @return + */ + protected int getMaximumPacketSize() { + return 32768; + } + + /** + * + * + * @return + */ + public String getOriginatingHost() { + return channel.getOriginatingHost(); + } + + /** + * + * + * @return + */ + public int getOriginatingPort() { + return channel.getOriginatingPort(); + } + + /** + * + * + * @return + */ + public String getHostToConnectOrBind() { + return channel.getHostToConnectOrBind(); + } + + /** + * + * + * @return + */ + public int getPortToConnectOrBind() { + return channel.getPortToConnectOrBind(); + } + + /** + * + * + * @param request + * @param wantReply + * @param requestData + * + * @throws IOException + */ + protected void onChannelRequest(String request, boolean wantReply, + byte[] requestData) throws IOException { + connection.sendChannelRequestFailure(this); + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingChannel.java b/src/com/sshtools/j2ssh/forwarding/ForwardingChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..29b98a9ec12f98a91bf5b47cdf3669e9007af56d --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingChannel.java @@ -0,0 +1,81 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.42 $ + */ +public interface ForwardingChannel { + /** */ + public final static String X11_FORWARDING_CHANNEL = "x11"; + + /** */ + public final static String LOCAL_FORWARDING_CHANNEL = "direct-tcpip"; + + /** */ + public final static String REMOTE_FORWARDING_CHANNEL = "forwarded-tcpip"; + + /** + * + * + * @return + */ + public String getChannelType(); + + /** + * + * + * @return + */ + public String getOriginatingHost(); + + /** + * + * + * @return + */ + public int getOriginatingPort(); + + /** + * + * + * @return + */ + public String getHostToConnectOrBind(); + + /** + * + * + * @return + */ + public int getPortToConnectOrBind(); + + public String getName(); +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingChannelImpl.java b/src/com/sshtools/j2ssh/forwarding/ForwardingChannelImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..cb390ce9f1bfd7ea3430893422ccf65766746695 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingChannelImpl.java @@ -0,0 +1,158 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.11 $ + */ +public class ForwardingChannelImpl implements ForwardingChannel { + private static Log log = LogFactory.getLog(ForwardingChannelImpl.class); + private String forwardType; + private String originatingHost; + private int originatingPort; + private String hostToConnectOrBind; + private int portToConnectOrBind; + private String name; + + /** + * Creates a new ForwardingChannelImpl object. + * + * @param forwardType + * @param hostToConnectOrBind + * @param portToConnectOrBind + * @param originatingHost + * @param originatingPort + * + * @throws ForwardingConfigurationException + */ + public ForwardingChannelImpl(String forwardType, String name, /*ForwardingConfiguration config,*/ + String hostToConnectOrBind, int portToConnectOrBind, + String originatingHost, int originatingPort) + throws ForwardingConfigurationException { + if (!forwardType.equals(LOCAL_FORWARDING_CHANNEL) && + !forwardType.equals(REMOTE_FORWARDING_CHANNEL) && + !forwardType.equals(X11_FORWARDING_CHANNEL)) { + throw new ForwardingConfigurationException( + "The forwarding type is invalid"); + } + + //this.config = config; + this.forwardType = forwardType; + this.hostToConnectOrBind = hostToConnectOrBind; + this.portToConnectOrBind = portToConnectOrBind; + this.originatingHost = originatingHost; + this.originatingPort = originatingPort; + this.name = name; + } + + public String getName() { + return name; + } + + /** + * + * + * @return + */ + public String getHostToConnectOrBind() { + return hostToConnectOrBind; + } + + /** + * + * + * @return + */ + public int getPortToConnectOrBind() { + return portToConnectOrBind; + } + + /** + * + * + * @return + */ + public byte[] getChannelOpenData() { + try { + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(hostToConnectOrBind); + baw.writeInt(portToConnectOrBind); + baw.writeString(originatingHost); + baw.writeInt(originatingPort); + + return baw.toByteArray(); + } catch (IOException ioe) { + return null; + } + } + + /** + * + * + * @return + */ + public byte[] getChannelConfirmationData() { + return null; + } + + /** + * + * + * @return + */ + public String getChannelType() { + return forwardType; + } + + /** + * + * + * @return + */ + public String getOriginatingHost() { + return originatingHost; + } + + /** + * + * + * @return + */ + public int getOriginatingPort() { + return originatingPort; + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingClient.java b/src/com/sshtools/j2ssh/forwarding/ForwardingClient.java new file mode 100644 index 0000000000000000000000000000000000000000..e396477b077d97949df3fcca12ce515ac704ab92 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingClient.java @@ -0,0 +1,830 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import com.sshtools.j2ssh.configuration.SshConnectionProperties; +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelFactory; +import com.sshtools.j2ssh.connection.ConnectionProtocol; +import com.sshtools.j2ssh.connection.InvalidChannelException; +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.util.StartStopState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.net.Socket; +import java.net.SocketPermission; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.37 $ + */ +public class ForwardingClient implements ChannelFactory { + private static Log log = LogFactory.getLog(ForwardingClient.class); + + /** */ + public final static String REMOTE_FORWARD_REQUEST = "tcpip-forward"; + + /** */ + public final static String REMOTE_FORWARD_CANCEL_REQUEST = "cancel-tcpip-forward"; + private ConnectionProtocol connection; + private List channelTypes = new Vector(); + private Map localForwardings = new HashMap(); + private Map remoteForwardings = new HashMap(); + private XDisplay xDisplay; + private ForwardingConfiguration x11ForwardingConfiguration; + + /** + * Creates a new ForwardingClient object. + * + * @param connection + * + * @throws IOException + */ + public ForwardingClient(ConnectionProtocol connection) + throws IOException { + this.connection = connection; + + //channelTypes.add(ForwardingSocketChannel.REMOTE_FORWARDING_CHANNEL); + connection.addChannelFactory(ForwardingSocketChannel.REMOTE_FORWARDING_CHANNEL, + this); + connection.addChannelFactory(ForwardingSocketChannel.X11_FORWARDING_CHANNEL, + this); + } + + /** + * + * + * @return + */ + public List getChannelType() { + return channelTypes; + } + + /** + * + * + * @param localDisplay + */ + public void enableX11Forwarding(XDisplay localDisplay) { + xDisplay = localDisplay; + x11ForwardingConfiguration = new ForwardingConfiguration("x11", "", 0, + xDisplay.getHost(), xDisplay.getPort()); + } + + /** + * + * + * @return + */ + public ForwardingConfiguration getX11ForwardingConfiguration() { + return x11ForwardingConfiguration; + } + + /** + * + * + * @return + */ + public boolean hasActiveConfigurations() { + // First check the size + if ((localForwardings.size() == 0) && (remoteForwardings.size() == 0)) { + return false; + } + + Iterator it = localForwardings.values().iterator(); + + while (it.hasNext()) { + if (((ForwardingConfiguration) it.next()).getState().getValue() == StartStopState.STARTED) { + return true; + } + } + + it = remoteForwardings.values().iterator(); + + while (it.hasNext()) { + if (((ForwardingConfiguration) it.next()).getState().getValue() == StartStopState.STARTED) { + return true; + } + } + + return false; + } + + public void synchronizeConfiguration(SshConnectionProperties properties) { + ForwardingConfiguration fwd = null; + + if (properties.getLocalForwardings().size() > 0) { + for (Iterator it = properties.getLocalForwardings().values() + .iterator(); it.hasNext();) { + try { + fwd = (ForwardingConfiguration) it.next(); + fwd = addLocalForwarding(fwd); + + if (properties.getForwardingAutoStartMode()) { + startLocalForwarding(fwd.getName()); + } + } catch (Throwable ex) { + log.warn((("Failed to start local forwarding " + fwd) != null) + ? fwd.getName() : "", ex); + } + } + } + + if (properties.getRemoteForwardings().size() > 0) { + for (Iterator it = properties.getRemoteForwardings().values() + .iterator(); it.hasNext();) { + try { + fwd = (ForwardingConfiguration) it.next(); + addRemoteForwarding(fwd); + + if (properties.getForwardingAutoStartMode()) { + startRemoteForwarding(fwd.getName()); + } + } catch (Throwable ex) { + log.warn((("Failed to start remote forwarding " + fwd) != null) + ? fwd.getName() : "", ex); + } + } + } + } + + /** + * + * + * @return + */ + public boolean hasActiveForwardings() { + // First check the size + if ((localForwardings.size() == 0) && (remoteForwardings.size() == 0)) { + return false; + } + + Iterator it = localForwardings.values().iterator(); + + while (it.hasNext()) { + if (((ForwardingConfiguration) it.next()).getActiveForwardingSocketChannels() + .size() > 0) { + return true; + } + } + + it = remoteForwardings.values().iterator(); + + while (it.hasNext()) { + if (((ForwardingConfiguration) it.next()).getActiveForwardingSocketChannels() + .size() > 0) { + return true; + } + } + + return false; + } + + /** + * + * + * @param addressToBind + * @param portToBind + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingConfiguration getLocalForwardingByAddress( + String addressToBind, int portToBind) + throws ForwardingConfigurationException { + Iterator it = localForwardings.values().iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(addressToBind) && + (config.getPortToBind() == portToBind)) { + return config; + } + } + + throw new ForwardingConfigurationException( + "The configuration does not exist"); + } + + /** + * + * + * @param name + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingConfiguration getLocalForwardingByName(String name) + throws ForwardingConfigurationException { + if (!localForwardings.containsKey(name)) { + throw new ForwardingConfigurationException( + "The configuraiton does not exist!"); + } + + return (ForwardingConfiguration) localForwardings.get(name); + } + + /** + * + * + * @param name + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingConfiguration getRemoteForwardingByName(String name) + throws ForwardingConfigurationException { + if (!remoteForwardings.containsKey(name)) { + throw new ForwardingConfigurationException( + "The configuraiton does not exist!"); + } + + return (ForwardingConfiguration) remoteForwardings.get(name); + } + + /** + * + * + * @return + */ + public Map getLocalForwardings() { + return localForwardings; + } + + /** + * + * + * @return + */ + public Map getRemoteForwardings() { + return remoteForwardings; + } + + /** + * + * + * @param addressToBind + * @param portToBind + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingConfiguration getRemoteForwardingByAddress( + String addressToBind, int portToBind) + throws ForwardingConfigurationException { + Iterator it = remoteForwardings.values().iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(addressToBind) && + (config.getPortToBind() == portToBind)) { + return config; + } + } + + throw new ForwardingConfigurationException( + "The configuration does not exist"); + } + + /** + * + * + * @param name + * + * @throws ForwardingConfigurationException + */ + public void removeLocalForwarding(String name) + throws ForwardingConfigurationException { + if (!localForwardings.containsKey(name)) { + throw new ForwardingConfigurationException( + "The name is not a valid forwarding configuration"); + } + + ForwardingListener listener = (ForwardingListener) localForwardings.get(name); + + if (listener.isRunning()) { + stopLocalForwarding(name); + } + + localForwardings.remove(name); + } + + /** + * + * + * @param name + * + * @throws IOException + * @throws ForwardingConfigurationException + */ + public void removeRemoteForwarding(String name) + throws IOException, ForwardingConfigurationException { + if (!remoteForwardings.containsKey(name)) { + throw new ForwardingConfigurationException( + "The name is not a valid forwarding configuration"); + } + + ForwardingListener listener = (ForwardingListener) remoteForwardings.get(name); + + if (listener.isRunning()) { + stopRemoteForwarding(name); + } + + remoteForwardings.remove(name); + } + + /** + * + * + * @param uniqueName + * @param addressToBind + * @param portToBind + * @param hostToConnect + * @param portToConnect + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingConfiguration addLocalForwarding(String uniqueName, + String addressToBind, int portToBind, String hostToConnect, + int portToConnect) throws ForwardingConfigurationException { + // Check that the name does not exist + if (localForwardings.containsKey(uniqueName)) { + throw new ForwardingConfigurationException( + "The configuration name already exists!"); + } + + // Check that the address to bind and port are not already being used + Iterator it = localForwardings.values().iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(addressToBind) && + (config.getPortToBind() == portToBind)) { + throw new ForwardingConfigurationException( + "The address and port are already in use"); + } + } + + // Check the security mananger + SecurityManager manager = System.getSecurityManager(); + + if (manager != null) { + try { + manager.checkPermission(new SocketPermission(addressToBind + + ":" + String.valueOf(portToBind), "accept,listen")); + } catch (SecurityException e) { + throw new ForwardingConfigurationException( + "The security manager has denied listen permision on " + + addressToBind + ":" + String.valueOf(portToBind)); + } + } + + // Create the configuration object + ForwardingConfiguration cf = new ClientForwardingListener(uniqueName, + connection, addressToBind, portToBind, hostToConnect, + portToConnect); + localForwardings.put(uniqueName, cf); + + return cf; + } + + /** + * + * + * @param fwd + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingConfiguration addLocalForwarding( + ForwardingConfiguration fwd) throws ForwardingConfigurationException { + return addLocalForwarding(fwd.getName(), fwd.getAddressToBind(), + fwd.getPortToBind(), fwd.getHostToConnect(), fwd.getPortToConnect()); + + /* // Check that the name does not exist + if (localForwardings.containsKey(fwd.getName())) { + throw new ForwardingConfigurationException( + "The configuration name already exists!"); + } + // Check that the address to bind and port are not already being used + Iterator it = localForwardings.values().iterator(); + ForwardingConfiguration config; + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + if (config.getAddressToBind().equals(fwd.getAddressToBind()) + && (config.getPortToBind() == fwd.getPortToBind())) { + throw new ForwardingConfigurationException( + "The address and port are already in use"); + } + } + // Check the security mananger + SecurityManager manager = System.getSecurityManager(); + if (manager != null) { + try { + manager.checkPermission(new SocketPermission(fwd + .getAddressToBind() + ":" + + String.valueOf(fwd.getPortToBind()), "accept,listen")); + } catch (SecurityException e) { + throw new ForwardingConfigurationException( + "The security manager has denied listen permision on " + + fwd.getAddressToBind() + ":" + + String.valueOf(fwd.getPortToBind())); + } + } + // Create the configuration object + localForwardings.put(fwd.getName(), + new ClientForwardingListener(fwd.getName(), connection, + fwd.getAddressToBind(), fwd.getPortToBind(), + fwd.getHostToConnect(), fwd.getPortToConnect()));*/ + } + + /** + * + * + * @param uniqueName + * @param addressToBind + * @param portToBind + * @param hostToConnect + * @param portToConnect + * + * @throws ForwardingConfigurationException + */ + public void addRemoteForwarding(String uniqueName, String addressToBind, + int portToBind, String hostToConnect, int portToConnect) + throws ForwardingConfigurationException { + // Check that the name does not exist + if (remoteForwardings.containsKey(uniqueName)) { + throw new ForwardingConfigurationException( + "The remote forwaring configuration name already exists!"); + } + + // Check that the address to bind and port are not already being used + Iterator it = remoteForwardings.values().iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(addressToBind) && + (config.getPortToBind() == portToBind)) { + throw new ForwardingConfigurationException( + "The remote forwarding address and port are already in use"); + } + } + + // Check the security mananger + SecurityManager manager = System.getSecurityManager(); + + if (manager != null) { + try { + manager.checkPermission(new SocketPermission(hostToConnect + + ":" + String.valueOf(portToConnect), "connect")); + } catch (SecurityException e) { + throw new ForwardingConfigurationException( + "The security manager has denied connect permision on " + + hostToConnect + ":" + String.valueOf(portToConnect)); + } + } + + // Create the configuration object + remoteForwardings.put(uniqueName, + new ForwardingConfiguration(uniqueName, addressToBind, portToBind, + hostToConnect, portToConnect)); + } + + /** + * + * + * @param fwd + * + * @throws ForwardingConfigurationException + */ + public void addRemoteForwarding(ForwardingConfiguration fwd) + throws ForwardingConfigurationException { + // Check that the name does not exist + if (remoteForwardings.containsKey(fwd.getName())) { + throw new ForwardingConfigurationException( + "The remote forwaring configuration name already exists!"); + } + + // Check that the address to bind and port are not already being used + Iterator it = remoteForwardings.values().iterator(); + ForwardingConfiguration config; + + while (it.hasNext()) { + config = (ForwardingConfiguration) it.next(); + + if (config.getAddressToBind().equals(fwd.getAddressToBind()) && + (config.getPortToBind() == fwd.getPortToBind())) { + throw new ForwardingConfigurationException( + "The remote forwarding address and port are already in use"); + } + } + + // Check the security mananger + SecurityManager manager = System.getSecurityManager(); + + if (manager != null) { + try { + manager.checkPermission(new SocketPermission(fwd.getHostToConnect() + + ":" + String.valueOf(fwd.getPortToConnect()), "connect")); + } catch (SecurityException e) { + throw new ForwardingConfigurationException( + "The security manager has denied connect permision on " + + fwd.getHostToConnect() + ":" + + String.valueOf(fwd.getPortToConnect())); + } + } + + // Create the configuration object + remoteForwardings.put(fwd.getName(), fwd); + } + + /** + * + * + * @param channelType + * @param requestData + * + * @return + * + * @throws InvalidChannelException + */ + public Channel createChannel(String channelType, byte[] requestData) + throws InvalidChannelException { + if (channelType.equals(ForwardingSocketChannel.X11_FORWARDING_CHANNEL)) { + if (xDisplay == null) { + throw new InvalidChannelException( + "Local display has not been set for X11 forwarding."); + } + + try { + ByteArrayReader bar = new ByteArrayReader(requestData); + String originatingHost = bar.readString(); + int originatingPort = (int) bar.readInt(); + log.debug("Creating socket to " + + x11ForwardingConfiguration.getHostToConnect() + "/" + + x11ForwardingConfiguration.getPortToConnect()); + + Socket socket = new Socket(x11ForwardingConfiguration.getHostToConnect(), + x11ForwardingConfiguration.getPortToConnect()); + + // Create the channel adding it to the active channels + ForwardingSocketChannel channel = x11ForwardingConfiguration.createForwardingSocketChannel(channelType, + x11ForwardingConfiguration.getHostToConnect(), + x11ForwardingConfiguration.getPortToConnect(), + originatingHost, originatingPort); + channel.bindSocket(socket); + channel.addEventListener(x11ForwardingConfiguration.monitor); + + return channel; + } catch (IOException ioe) { + throw new InvalidChannelException(ioe.getMessage()); + } + } + + if (channelType.equals( + ForwardingSocketChannel.REMOTE_FORWARDING_CHANNEL)) { + try { + ByteArrayReader bar = new ByteArrayReader(requestData); + String addressBound = bar.readString(); + int portBound = (int) bar.readInt(); + String originatingHost = bar.readString(); + int originatingPort = (int) bar.readInt(); + ForwardingConfiguration config = getRemoteForwardingByAddress(addressBound, + portBound); + Socket socket = new Socket(config.getHostToConnect(), + config.getPortToConnect()); + + /*Socket socket = new Socket(); + socket.connect(new InetSocketAddress( + config.getHostToConnect(), config.getPortToConnect()));*/ + + // Create the channel adding it to the active channels + ForwardingSocketChannel channel = config.createForwardingSocketChannel(channelType, + config.getHostToConnect(), config.getPortToConnect(), + originatingHost, originatingPort); + channel.bindSocket(socket); + channel.addEventListener(config.monitor); + + return channel; + } catch (ForwardingConfigurationException fce) { + throw new InvalidChannelException( + "No valid forwarding configuration was available for the request address"); + } catch (IOException ioe) { + throw new InvalidChannelException(ioe.getMessage()); + } + } + + throw new InvalidChannelException( + "The server can only request a remote forwarding channel or an" + + "X11 forwarding channel"); + } + + /** + * + * + * @param uniqueName + * + * @throws ForwardingConfigurationException + */ + public void startLocalForwarding(String uniqueName) + throws ForwardingConfigurationException { + if (!localForwardings.containsKey(uniqueName)) { + throw new ForwardingConfigurationException( + "The name is not a valid forwarding configuration"); + } + + try { + ForwardingListener listener = (ForwardingListener) localForwardings.get(uniqueName); + listener.start(); + } catch (IOException ex) { + throw new ForwardingConfigurationException(ex.getMessage()); + } + } + + /** + * + * + * @throws IOException + * @throws ForwardingConfigurationException + */ + public void startX11Forwarding() + throws IOException, ForwardingConfigurationException { + if (x11ForwardingConfiguration == null) { + throw new ForwardingConfigurationException( + "X11 forwarding hasn't been enabled."); + } + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(x11ForwardingConfiguration.getAddressToBind()); + baw.writeInt(x11ForwardingConfiguration.getPortToBind()); + x11ForwardingConfiguration.getState().setValue(StartStopState.STARTED); + + if (log.isDebugEnabled()) { + log.info("X11 forwarding started"); + log.debug("Address to bind: " + + x11ForwardingConfiguration.getAddressToBind()); + log.debug("Port to bind: " + + String.valueOf(x11ForwardingConfiguration.getPortToBind())); + log.debug("Host to connect: " + + x11ForwardingConfiguration.hostToConnect); + log.debug("Port to connect: " + + x11ForwardingConfiguration.portToConnect); + } else { + log.info("Request for X11 rejected."); + } + } + + /** + * + * + * @param name + * + * @throws IOException + * @throws ForwardingConfigurationException + */ + public void startRemoteForwarding(String name) + throws IOException, ForwardingConfigurationException { + if (!remoteForwardings.containsKey(name)) { + throw new ForwardingConfigurationException( + "The name is not a valid forwarding configuration"); + } + + ForwardingConfiguration config = (ForwardingConfiguration) remoteForwardings.get(name); + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(config.getAddressToBind()); + baw.writeInt(config.getPortToBind()); + connection.sendGlobalRequest(REMOTE_FORWARD_REQUEST, true, + baw.toByteArray()); + remoteForwardings.put(name, config); + config.getState().setValue(StartStopState.STARTED); + log.info("Remote forwarding configuration '" + name + "' started"); + + if (log.isDebugEnabled()) { + log.debug("Address to bind: " + config.getAddressToBind()); + log.debug("Port to bind: " + + String.valueOf(config.getPortToBind())); + log.debug("Host to connect: " + config.hostToConnect); + log.debug("Port to connect: " + config.portToConnect); + } + } + + /** + * + * + * @param uniqueName + * + * @throws ForwardingConfigurationException + */ + public void stopLocalForwarding(String uniqueName) + throws ForwardingConfigurationException { + if (!localForwardings.containsKey(uniqueName)) { + throw new ForwardingConfigurationException( + "The name is not a valid forwarding configuration"); + } + + ForwardingListener listener = (ForwardingListener) localForwardings.get(uniqueName); + listener.stop(); + log.info("Local forwarding configuration " + uniqueName + "' stopped"); + } + + /** + * + * + * @param name + * + * @throws IOException + * @throws ForwardingConfigurationException + */ + public void stopRemoteForwarding(String name) + throws IOException, ForwardingConfigurationException { + if (!remoteForwardings.containsKey(name)) { + throw new ForwardingConfigurationException( + "The remote forwarding configuration does not exist"); + } + + ForwardingConfiguration config = (ForwardingConfiguration) remoteForwardings.get(name); + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(config.getAddressToBind()); + baw.writeInt(config.getPortToBind()); + connection.sendGlobalRequest(REMOTE_FORWARD_CANCEL_REQUEST, true, + baw.toByteArray()); + config.getState().setValue(StartStopState.STOPPED); + log.info("Remote forwarding configuration '" + name + "' stopped"); + } + + public class ClientForwardingListener extends ForwardingListener { + public ClientForwardingListener(String name, + ConnectionProtocol connection, String addressToBind, + int portToBind, String hostToConnect, int portToConnect) { + super(name, connection, addressToBind, portToBind, hostToConnect, + portToConnect); + } + + public ForwardingSocketChannel createChannel(String hostToConnect, + int portToConnect, Socket socket) + throws ForwardingConfigurationException { + return createForwardingSocketChannel(ForwardingSocketChannel.LOCAL_FORWARDING_CHANNEL, + hostToConnect, portToConnect, + + /*( (InetSocketAddress) socket. + getRemoteSocketAddress()).getAddress() + .getHostAddress()*/ + socket.getInetAddress().getHostAddress(), + /*( (InetSocketAddress) socket. + getRemoteSocketAddress()).getPort()*/ + socket.getPort()); + } + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingConfiguration.java b/src/com/sshtools/j2ssh/forwarding/ForwardingConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..9bc79f132e85e5bf7c35392e50d488e1cddea50d --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingConfiguration.java @@ -0,0 +1,417 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelEventListener; +import com.sshtools.j2ssh.util.StartStopState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.List; +import java.util.Vector; + +import javax.swing.event.EventListenerList; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.41 $ + */ +public class ForwardingConfiguration { + private static Log log = LogFactory.getLog(ForwardingConfiguration.class); + + /** */ + protected StartStopState state = new StartStopState(StartStopState.STOPPED); + + /** */ + protected String addressToBind; + + /** */ + protected String hostToConnect; + + /** */ + protected String name; + + /** */ + protected int portToBind; + + /** */ + protected int portToConnect; + + /** */ + protected ForwardingConfigurationMonitor monitor = new ForwardingConfigurationMonitor(); + + /** */ + protected EventListenerList listenerList = new EventListenerList(); + private List activeForwardings = new Vector(); + + /** + * Creates a new ForwardingConfiguration object. + * + * @param name + * @param addressToBind + * @param portToBind + * @param hostToConnect + * @param portToConnect + */ + public ForwardingConfiguration(String name, String addressToBind, + int portToBind, String hostToConnect, int portToConnect) { + this.addressToBind = addressToBind; + this.portToBind = portToBind; + this.name = name; + this.hostToConnect = hostToConnect; + this.portToConnect = portToConnect; + } + + /** + * Creates a new ForwardingConfiguration object. + * + * @param addressToBind + * @param portToBind + */ + public ForwardingConfiguration(String addressToBind, int portToBind) { + this(addressToBind + ":" + String.valueOf(portToBind), addressToBind, + portToBind, "[Specified by connecting computer]", -1); + } + + /** + * + * + * @param l + */ + public void addForwardingConfigurationListener( + ForwardingConfigurationListener l) { + listenerList.add(ForwardingConfigurationListener.class, l); + } + + /** + * + * + * @param l + */ + public void removeForwardingConfigurationListener( + ForwardingConfigurationListener l) { + listenerList.remove(ForwardingConfigurationListener.class, l); + } + + /** + * + * + * @return + */ + public List getActiveForwardingSocketChannels() { + return activeForwardings; + } + + public boolean isForwarding() { + return state.getValue() == StartStopState.STARTED; + } + + /** + * + * + * @return + */ + public String getAddressToBind() { + return addressToBind; + } + + /** + * + * + * @return + */ + public String getHostToConnect() { + return hostToConnect; + } + + /** + * + * + * @return + */ + public String getName() { + return name; + } + + /** + * + * + * @return + */ + public int getPortToBind() { + return portToBind; + } + + /** + * + * + * @return + */ + public int getPortToConnect() { + return portToConnect; + } + + /** + * + * + * @return + */ + public StartStopState getState() { + return state; + } + + /** + * + * + * @throws IOException + */ + public void start() throws IOException { + state.setValue(StartStopState.STARTED); + } + + /** + * + */ + public void stop() { + state.setValue(StartStopState.STOPPED); + } + + /** + * + * + * @param type + * @param hostToConnect + * @param portToConnect + * @param originatingHost + * @param originatingPort + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingSocketChannel createForwardingSocketChannel(String type, + String hostToConnect, int portToConnect, String originatingHost, + int originatingPort) throws ForwardingConfigurationException { + if (state.getValue() == StartStopState.STOPPED) { + throw new ForwardingConfigurationException( + "The forwarding has been stopped"); + } + + if (!type.equals(ForwardingChannel.LOCAL_FORWARDING_CHANNEL) && + !type.equals(ForwardingChannel.REMOTE_FORWARDING_CHANNEL) && + !type.equals(ForwardingChannel.X11_FORWARDING_CHANNEL)) { + throw new ForwardingConfigurationException( + "The channel type must either be " + + "ForwardingSocketChannel.LOCAL_FORWARDING_CHANNEL_TYPE or " + + "ForwardingSocketChannel.REMOTE_FORWARDING_CHANNEL_TYPE"); + } + + ForwardingSocketChannel channel; + + if (type.equals(ForwardingChannel.LOCAL_FORWARDING_CHANNEL)) { + channel = new ForwardingSocketChannel(type, name, hostToConnect, + portToConnect, originatingHost, originatingPort); + } else { + channel = new ForwardingSocketChannel(type, name, + getAddressToBind(), getPortToBind(), originatingHost, + originatingPort); + } + + channel.addEventListener(monitor); + + return channel; + } + + /** + * + * + * @param type + * @param hostToConnect + * @param portToConnect + * @param originatingHost + * @param originatingPort + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingIOChannel createForwardingIOChannel(String type, + String hostToConnect, int portToConnect, String originatingHost, + int originatingPort) throws ForwardingConfigurationException { + if (state.getValue() == StartStopState.STOPPED) { + throw new ForwardingConfigurationException( + "The forwarding has been stopped"); + } + + if (!type.equals(ForwardingChannel.LOCAL_FORWARDING_CHANNEL) && + !type.equals(ForwardingChannel.REMOTE_FORWARDING_CHANNEL) && + !type.equals(ForwardingChannel.X11_FORWARDING_CHANNEL)) { + throw new ForwardingConfigurationException( + "The channel type must either be " + + "ForwardingSocketChannel.LOCAL_FORWARDING_CHANNEL_TYPE or " + + "ForwardingSocketChannel.REMOTE_FORWARDING_CHANNEL_TYPE"); + } + + ForwardingIOChannel channel; + + if (type.equals(ForwardingChannel.LOCAL_FORWARDING_CHANNEL)) { + channel = new ForwardingIOChannel(type, name, getHostToConnect(), + getPortToConnect(), originatingHost, originatingPort); + } else { + channel = new ForwardingIOChannel(type, name, getAddressToBind(), + getPortToBind(), originatingHost, originatingPort); + } + + channel.addEventListener(monitor); + + return channel; + } + + /** + * + * + * @param type + * @param hostToConnect + * @param portToConnect + * @param originatingHost + * @param originatingPort + * + * @return + * + * @throws ForwardingConfigurationException + */ + public ForwardingBindingChannel createForwardingBindingChannel( + String type, String hostToConnect, int portToConnect, + String originatingHost, int originatingPort) + throws ForwardingConfigurationException { + if (state.getValue() == StartStopState.STOPPED) { + throw new ForwardingConfigurationException( + "The forwarding has been stopped"); + } + + if (!type.equals(ForwardingChannel.LOCAL_FORWARDING_CHANNEL) && + !type.equals(ForwardingChannel.REMOTE_FORWARDING_CHANNEL) && + !type.equals(ForwardingChannel.X11_FORWARDING_CHANNEL)) { + throw new ForwardingConfigurationException( + "The channel type must either be " + + "ForwardingSocketChannel.LOCAL_FORWARDING_CHANNEL_TYPE or " + + "ForwardingSocketChannel.REMOTE_FORWARDING_CHANNEL_TYPE"); + } + + ForwardingBindingChannel channel; + + if (type.equals(ForwardingChannel.LOCAL_FORWARDING_CHANNEL)) { + channel = new ForwardingBindingChannel(type, name, + getHostToConnect(), getPortToConnect(), originatingHost, + originatingPort); + } else { + channel = new ForwardingBindingChannel(type, name, + getAddressToBind(), getPortToBind(), originatingHost, + originatingPort); + } + + channel.addEventListener(monitor); + + return channel; + } + + public class ForwardingConfigurationMonitor implements ChannelEventListener { + public void onChannelOpen(Channel channel) { + if (log.isDebugEnabled()) { + ForwardingChannel fch = (ForwardingChannel) channel; + log.debug("Opening forwarding channel from " + + fch.getOriginatingHost() + ":" + + String.valueOf(fch.getOriginatingPort())); + } + + // Add channel to the active forwardings + activeForwardings.add(channel); + + ForwardingConfigurationListener[] l = (ForwardingConfigurationListener[]) listenerList.getListeners(ForwardingConfigurationListener.class); + + for (int i = (l.length - 1); i >= 0; i--) { + l[i].opened(ForwardingConfiguration.this, + (ForwardingSocketChannel) channel); + } + } + + public void onChannelEOF(Channel channel) { + // Close the OutputStream to force the channel to close + + /* try { + //channel.getOutputStream().close(); + //channel.getInputStream().close(); + channel.close(); + } + catch (IOException ex) { + }*/ + } + + public void onChannelClose(Channel channel) { + if (log.isDebugEnabled()) { + ForwardingChannel fch = (ForwardingChannel) channel; + log.debug("Closing forwarding channel from " + + fch.getOriginatingHost() + ":" + + String.valueOf(fch.getOriginatingPort())); + } + + // Remove channel from the active forwardings + activeForwardings.remove(channel); + + ForwardingConfigurationListener[] l = (ForwardingConfigurationListener[]) listenerList.getListeners(ForwardingConfigurationListener.class); + + for (int i = (l.length - 1); i >= 0; i--) { + l[i].closed(ForwardingConfiguration.this, + (ForwardingSocketChannel) channel); + } + } + + public void onDataReceived(Channel channel, byte[] data) { + ForwardingConfigurationListener[] l = (ForwardingConfigurationListener[]) listenerList.getListeners(ForwardingConfigurationListener.class); + + for (int i = (l.length - 1); i >= 0; i--) { + l[i].dataReceived(ForwardingConfiguration.this, + (ForwardingSocketChannel) channel, data.length); + } + } + + public void onDataSent(Channel channel, byte[] data) { + ForwardingConfigurationListener[] l = (ForwardingConfigurationListener[]) listenerList.getListeners(ForwardingConfigurationListener.class); + + for (int i = (l.length - 1); i >= 0; i--) { + l[i].dataSent(ForwardingConfiguration.this, + (ForwardingSocketChannel) channel, data.length); + } + } + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingConfigurationException.java b/src/com/sshtools/j2ssh/forwarding/ForwardingConfigurationException.java new file mode 100644 index 0000000000000000000000000000000000000000..03c13c03b51ec767863d5c104c9950a1c02d3697 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingConfigurationException.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class ForwardingConfigurationException extends IOException { + /** + * Creates a new ForwardingConfigurationException object. + * + * @param msg + */ + public ForwardingConfigurationException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingConfigurationListener.java b/src/com/sshtools/j2ssh/forwarding/ForwardingConfigurationListener.java new file mode 100644 index 0000000000000000000000000000000000000000..3c520d83e712e39ddf94f04e52530aa914bd7eb0 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingConfigurationListener.java @@ -0,0 +1,71 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import java.util.EventListener; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public interface ForwardingConfigurationListener extends EventListener { + /** + * + * + * @param channel + */ + public void opened(ForwardingConfiguration forward, + ForwardingChannel channel); + + /** + * + * + * @param channel + */ + public void closed(ForwardingConfiguration forward, + ForwardingChannel channel); + + /** + * + * + * @param channel + * @param bytes + */ + public void dataReceived(ForwardingConfiguration forward, + ForwardingChannel channel, int bytes); + + /** + * + * + * @param channel + * @param bytes + */ + public void dataSent(ForwardingConfiguration forward, + ForwardingChannel channel, int bytes); +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingIOChannel.java b/src/com/sshtools/j2ssh/forwarding/ForwardingIOChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..9a0d04286cabe8618812c5c57f0e49ad5abf1641 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingIOChannel.java @@ -0,0 +1,189 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import com.sshtools.j2ssh.connection.IOChannel; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +//import java.net.InetSocketAddress; +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ForwardingIOChannel extends IOChannel implements ForwardingChannel { + private static Log log = LogFactory.getLog(ForwardingIOChannel.class); + private ForwardingChannelImpl channel; + + /** + * Creates a new ForwardingIOChannel object. + * + * @param forwardType + * @param hostToConnectOrBind + * @param portToConnectOrBind + * @param originatingHost + * @param originatingPort + * + * @throws ForwardingConfigurationException + */ + public ForwardingIOChannel(String forwardType, String name, /*ForwardingConfiguration config,*/ + String hostToConnectOrBind, int portToConnectOrBind, + String originatingHost, int originatingPort) + throws ForwardingConfigurationException { + if (!forwardType.equals(LOCAL_FORWARDING_CHANNEL) && + !forwardType.equals(REMOTE_FORWARDING_CHANNEL) && + !forwardType.equals(X11_FORWARDING_CHANNEL)) { + throw new ForwardingConfigurationException( + "The forwarding type is invalid"); + } + + channel = new ForwardingChannelImpl(forwardType, name, + hostToConnectOrBind, portToConnectOrBind, originatingHost, + originatingPort); + } + + /** + * + * + * @return + */ + public byte[] getChannelOpenData() { + return channel.getChannelOpenData(); + } + + /** + * + * + * @return + */ + public byte[] getChannelConfirmationData() { + return channel.getChannelConfirmationData(); + } + + public String getName() { + return channel.getName(); + } + + /** + * + * + * @return + */ + public String getChannelType() { + return channel.getChannelType(); + } + + /** + * + * + * @return + */ + protected int getMinimumWindowSpace() { + return 32768; + } + + /** + * + * + * @return + */ + protected int getMaximumWindowSpace() { + return 131072; + } + + /** + * + * + * @return + */ + protected int getMaximumPacketSize() { + return 32768; + } + + /** + * + * + * @return + */ + public String getOriginatingHost() { + return channel.getOriginatingHost(); + } + + /** + * + * + * @return + */ + public int getOriginatingPort() { + return channel.getOriginatingPort(); + } + + /** + * + * + * @return + */ + public String getHostToConnectOrBind() { + return channel.getHostToConnectOrBind(); + } + + /** + * + * + * @return + */ + public int getPortToConnectOrBind() { + return channel.getPortToConnectOrBind(); + } + + /** + * + * + * @param request + * @param wantReply + * @param requestData + * + * @throws IOException + */ + protected void onChannelRequest(String request, boolean wantReply, + byte[] requestData) throws IOException { + connection.sendChannelRequestFailure(this); + } + + /** + * + * + * @throws IOException + */ + protected void onChannelOpen() throws IOException { + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingListener.java b/src/com/sshtools/j2ssh/forwarding/ForwardingListener.java new file mode 100644 index 0000000000000000000000000000000000000000..01189f154bfcd8c28903c72dcf8677180fcdb223 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingListener.java @@ -0,0 +1,236 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import com.sshtools.j2ssh.SshThread; +import com.sshtools.j2ssh.connection.ConnectionProtocol; +import com.sshtools.j2ssh.util.StartStopState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.30 $ + */ +public abstract class ForwardingListener extends ForwardingConfiguration + implements Runnable { + private static Log log = LogFactory.getLog(ForwardingListener.class); + private ConnectionProtocol connection; + private ServerSocket server; + private Thread thread; + private boolean listening; + + /** + * Creates a new ForwardingListener object. + * + * @param name + * @param connection + * @param addressToBind + * @param portToBind + * @param hostToConnect + * @param portToConnect + */ + public ForwardingListener(String name, ConnectionProtocol connection, + String addressToBind, int portToBind, String hostToConnect, + int portToConnect) { + super(name, addressToBind, portToBind, hostToConnect, portToConnect); + log.info("Creating forwarding listener named '" + name + "'"); + this.connection = connection; + + if (log.isDebugEnabled()) { + log.debug("Address to bind: " + getAddressToBind()); + log.debug("Port to bind: " + String.valueOf(getPortToBind())); + log.debug("Host to connect: " + hostToConnect); + log.debug("Port to connect: " + portToConnect); + } + } + + /** + * Creates a new ForwardingListener object. + * + * @param connection + * @param addressToBind + * @param portToBind + */ + public ForwardingListener(ConnectionProtocol connection, + String addressToBind, int portToBind) { + this(addressToBind + ":" + String.valueOf(portToBind), connection, + addressToBind, portToBind, "[Specified by connecting computer]", -1); + } + + /** + * + * + * @return + */ + public int getLocalPort() { + return (server == null) ? (-1) : server.getLocalPort(); + } + + /** + * + * + * @return + */ + public boolean isListening() { + return listening; + } + + /** + * + */ + public void run() { + try { + log.info("Starting forwarding listener thread for '" + name + "'"); + + // + // ServerSocket server = new ServerSocket(getPortToBind(), 50, InetAddress.getByName(getAddressToBind())); + //server = new ServerSocket(getPortToBind(), 50, InetAddress.getByName(getAddressToBind())); + Socket socket; + + while (state.getValue() == StartStopState.STARTED) { + listening = true; + socket = server.accept(); + + if ((state.getValue() == StartStopState.STOPPED) || + (socket == null)) { + break; + } + + log.info("Connection accepted, creating forwarding channel"); + + try { + ForwardingSocketChannel channel = createChannel(hostToConnect, + portToConnect, socket); + channel.bindSocket(socket); + + if (connection.openChannel(channel)) { + log.info("Forwarding channel for '" + name + + "' is open"); + } else { + log.warn("Failed to open forwarding chanel " + name); + socket.close(); + } + } catch (Exception ex) { + log.warn("Failed to open forwarding chanel " + name, ex); + + try { + socket.close(); + } catch (IOException ioe) { + } + } + } + } catch (IOException ioe) { + /* only warn if the forwarding has not been stopped */ + if (state.getValue() == StartStopState.STARTED) { + log.warn("Local forwarding listener to " + hostToConnect + ":" + + String.valueOf(portToConnect) + " has failed", ioe); + } + } finally { + stop(); + } + } + + /** + * + * + * @return + */ + public boolean isRunning() { + return (thread != null) && thread.isAlive(); + } + + /** + * + * + * @throws IOException + */ + public void start() throws IOException { + /* Set the state by calling the super method */ + super.start(); + + /* Bind server socket */ + try { + server = new ServerSocket(getPortToBind(), 50, + InetAddress.getByName(getAddressToBind())); + } catch (IOException ioe) { + super.stop(); + throw ioe; + } + + /* Create a thread and start it */ + thread = new SshThread(this, "Forwarding listener", true); + + /* Create a thread and start it */ + thread = new SshThread(this, "Forwarding listener", true); + thread.start(); + } + + /** + * + */ + public void stop() { + /* Set the state by calling the super method */ + super.stop(); + + try { + /* Close the server socket */ + if (server != null) { + server.close(); + } + } catch (IOException ioe) { + log.warn("Forwarding listener failed to stop", ioe); + } + + thread = null; + listening = false; + } + + /** + * + * + * @param hostToConnect + * @param portToConnect + * @param socket + * + * @return + * + * @throws ForwardingConfigurationException + */ + protected abstract ForwardingSocketChannel createChannel( + String hostToConnect, int portToConnect, Socket socket) + throws ForwardingConfigurationException; +} diff --git a/src/com/sshtools/j2ssh/forwarding/ForwardingSocketChannel.java b/src/com/sshtools/j2ssh/forwarding/ForwardingSocketChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..e99c827165084237eab533f3da1a85713baa2f79 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/ForwardingSocketChannel.java @@ -0,0 +1,184 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + +import com.sshtools.j2ssh.connection.SocketChannel; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +//import java.net.InetSocketAddress; +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ForwardingSocketChannel extends SocketChannel + implements ForwardingChannel { + private static Log log = LogFactory.getLog(ForwardingSocketChannel.class); + private ForwardingChannelImpl channel; + + /** + * Creates a new ForwardingSocketChannel object. + * + * @param forwardType + * @param hostToConnectOrBind + * @param portToConnectOrBind + * @param originatingHost + * @param originatingPort + * + * @throws ForwardingConfigurationException + */ + public ForwardingSocketChannel(String forwardType, String name, + + /*ForwardingConfiguration config,*/ + String hostToConnectOrBind, int portToConnectOrBind, + String originatingHost, int originatingPort) + throws ForwardingConfigurationException { + if (!forwardType.equals(LOCAL_FORWARDING_CHANNEL) && + !forwardType.equals(REMOTE_FORWARDING_CHANNEL) && + !forwardType.equals(X11_FORWARDING_CHANNEL)) { + throw new ForwardingConfigurationException( + "The forwarding type is invalid"); + } + + channel = new ForwardingChannelImpl(forwardType, name, + hostToConnectOrBind, portToConnectOrBind, originatingHost, + originatingPort); + } + + public String getName() { + return channel.getName(); + } + + /** + * + * + * @return + */ + public byte[] getChannelOpenData() { + return channel.getChannelOpenData(); + } + + /** + * + * + * @return + */ + public byte[] getChannelConfirmationData() { + return channel.getChannelConfirmationData(); + } + + /** + * + * + * @return + */ + public String getChannelType() { + return channel.getChannelType(); + } + + /** + * + * + * @return + */ + protected int getMinimumWindowSpace() { + return 32768; + } + + /** + * + * + * @return + */ + protected int getMaximumWindowSpace() { + return 131072; + } + + /** + * + * + * @return + */ + protected int getMaximumPacketSize() { + return 32768; + } + + /** + * + * + * @return + */ + public String getOriginatingHost() { + return channel.getOriginatingHost(); + } + + /** + * + * + * @return + */ + public int getOriginatingPort() { + return channel.getOriginatingPort(); + } + + /** + * + * + * @return + */ + public String getHostToConnectOrBind() { + return channel.getHostToConnectOrBind(); + } + + /** + * + * + * @return + */ + public int getPortToConnectOrBind() { + return channel.getPortToConnectOrBind(); + } + + /** + * + * + * @param request + * @param wantReply + * @param requestData + * + * @throws IOException + */ + protected void onChannelRequest(String request, boolean wantReply, + byte[] requestData) throws IOException { + connection.sendChannelRequestFailure(this); + } +} diff --git a/src/com/sshtools/j2ssh/forwarding/XDisplay.java b/src/com/sshtools/j2ssh/forwarding/XDisplay.java new file mode 100644 index 0000000000000000000000000000000000000000..a76ee10edb322146b92811348287dbe1914a65f8 --- /dev/null +++ b/src/com/sshtools/j2ssh/forwarding/XDisplay.java @@ -0,0 +1,193 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.forwarding; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class XDisplay { + private String host; + private int display; + private int screen; + private int portOffset; + + /** + * Creates a new XDisplay object. + * + * @param string + */ + public XDisplay(String string) { + this(string, 6000); + } + + /** + * Creates a new XDisplay object. + * + * @param string + * @param portOffset + */ + public XDisplay(String string, int portOffset) { + setString(string); + setPortOffset(portOffset); + } + + /** + * + * + * @param portOffset + */ + public void setPortOffset(int portOffset) { + this.portOffset = portOffset; + } + + /** + * + * + * @return + */ + public int getPortOffset() { + return portOffset; + } + + /** + * + * + * @param string + */ + public void setString(String string) { + int idx = string.indexOf(':'); + + if (idx == -1) { + display = 0; + host = string; + } else { + host = string.substring(0, idx); + + String s = string.substring(idx + 1); + idx = s.indexOf("."); + + if (idx == -1) { + screen = 0; + + try { + display = Integer.parseInt(s); + } catch (NumberFormatException nfe) { + display = 0; + } + } else { + try { + display = Integer.parseInt(s.substring(0, idx)); + } catch (NumberFormatException nfe) { + display = 0; + } + + try { + screen = Integer.parseInt(s.substring(idx + 1)); + } catch (NumberFormatException nfe) { + screen = 0; + } + } + } + } + + /** + * + * + * @return + */ + public int getPort() { + return (getDisplay() < getPortOffset()) + ? (getDisplay() + getPortOffset()) : getDisplay(); + } + + /** + * + * + * @return + */ + public int getScreen() { + return screen; + } + + /** + * + * + * @param host + */ + public void setHost(String host) { + this.host = host; + } + + /** + * + * + * @param display + */ + public void setDisplay(int display) { + this.display = display; + } + + /** + * + * + * @param screen + */ + public void setScreen(int screen) { + this.screen = screen; + } + + /** + * + * + * @return + */ + public String getHost() { + return host; + } + + /** + * + * + * @return + */ + public int getDisplay() { + return display; + } + + /** + * + * + * @return + */ + public String toString() { + return getHost() + ":" + getDisplay() + + ((getScreen() == 0) ? "" : ("." + getScreen())); + } +} diff --git a/src/com/sshtools/j2ssh/io/ByteArrayReader.java b/src/com/sshtools/j2ssh/io/ByteArrayReader.java new file mode 100644 index 0000000000000000000000000000000000000000..3875ad1dc5d8501529ae6ad09fbb4c9dba674a6a --- /dev/null +++ b/src/com/sshtools/j2ssh/io/ByteArrayReader.java @@ -0,0 +1,168 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import java.math.BigInteger; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class ByteArrayReader extends ByteArrayInputStream { + /** + * Creates a new ByteArrayReader object. + * + * @param data + */ + public ByteArrayReader(byte[] data) { + super(data); + } + + /** + * + * + * @param data + * @param start + * + * @return + */ + public static long readInt(byte[] data, int start) { + long ret = (((long) (data[start] & 0xFF) << 24) & 0xFFFFFFFF) | + ((data[start + 1] & 0xFF) << 16) | ((data[start + 2] & 0xFF) << 8) | + ((data[start + 3] & 0xFF) << 0); + + return ret; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public long readInt() throws IOException { + byte[] raw = new byte[4]; + read(raw); + + long ret = (((long) (raw[0] & 0xFF) << 24) & 0xFFFFFFFF) | + ((raw[1] & 0xFF) << 16) | ((raw[2] & 0xFF) << 8) | (raw[3] & 0xFF); + + return ret; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public UnsignedInteger32 readUINT32() throws IOException { + return new UnsignedInteger32(readInt()); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public UnsignedInteger64 readUINT64() throws IOException { + byte[] raw = new byte[8]; + read(raw); + + return new UnsignedInteger64(raw); + } + + /** + * + * + * @param data + * @param start + * + * @return + */ + public static String readString(byte[] data, int start) { + int len = (int) readInt(data, start); + byte[] chars = new byte[(int) len]; + System.arraycopy(data, start + 4, chars, 0, len); + + return new String(chars); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public BigInteger readBigInteger() throws IOException { + int len = (int) readInt(); + byte[] raw = new byte[len]; + read(raw); + + return new BigInteger(raw); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public byte[] readBinaryString() throws IOException { + long len = readInt(); + byte[] raw = new byte[(int) len]; + read(raw); + + return raw; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public String readString() throws IOException { + long len = readInt(); + byte[] raw = new byte[(int) len]; + read(raw); + + return new String(raw); + } +} diff --git a/src/com/sshtools/j2ssh/io/ByteArrayWriter.java b/src/com/sshtools/j2ssh/io/ByteArrayWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..98dd71ece96a46ddad528b05f6f13e0557635b25 --- /dev/null +++ b/src/com/sshtools/j2ssh/io/ByteArrayWriter.java @@ -0,0 +1,206 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import java.math.BigInteger; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public class ByteArrayWriter extends ByteArrayOutputStream { + /** + * Creates a new ByteArrayWriter object. + */ + public ByteArrayWriter() { + } + + /** + * + * + * @param bi + * + * @throws IOException + */ + public void writeBigInteger(BigInteger bi) throws IOException { + byte[] raw = bi.toByteArray(); + writeInt(raw.length); + write(raw); + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void writeBoolean(boolean b) throws IOException { + write(b ? 1 : 0); + } + + /** + * + * + * @param data + * + * @throws IOException + */ + public void writeBinaryString(byte[] data) throws IOException { + writeInt(data.length); + write(data); + } + + /** + * + * + * @param i + * + * @throws IOException + */ + public void writeInt(long i) throws IOException { + byte[] raw = new byte[4]; + raw[0] = (byte) (i >> 24); + raw[1] = (byte) (i >> 16); + raw[2] = (byte) (i >> 8); + raw[3] = (byte) (i); + write(raw); + } + + /** + * + * + * @param i + * + * @throws IOException + */ + public void writeInt(int i) throws IOException { + byte[] raw = new byte[4]; + raw[0] = (byte) (i >> 24); + raw[1] = (byte) (i >> 16); + raw[2] = (byte) (i >> 8); + raw[3] = (byte) (i); + write(raw); + } + + /** + * + * + * @param i + * + * @return + */ + public static byte[] encodeInt(int i) { + byte[] raw = new byte[4]; + raw[0] = (byte) (i >> 24); + raw[1] = (byte) (i >> 16); + raw[2] = (byte) (i >> 8); + raw[3] = (byte) (i); + + return raw; + } + + /** + * + * + * @param value + * + * @throws IOException + */ + public void writeUINT32(UnsignedInteger32 value) throws IOException { + writeInt(value.longValue()); + } + + /** + * + * + * @param value + * + * @throws IOException + */ + public void writeUINT64(UnsignedInteger64 value) throws IOException { + byte[] raw = new byte[8]; + byte[] bi = value.bigIntValue().toByteArray(); + System.arraycopy(bi, 0, raw, raw.length - bi.length, bi.length); + + // Pad the raw data + write(raw); + } + + /** + * + * + * @param array + * @param pos + * @param value + * + * @throws IOException + */ + public static void writeIntToArray(byte[] array, int pos, int value) + throws IOException { + if ((array.length - pos) < 4) { + throw new IOException( + "Not enough data in array to write integer at position " + + String.valueOf(pos)); + } + + array[pos] = (byte) (value >> 24); + array[pos + 1] = (byte) (value >> 16); + array[pos + 2] = (byte) (value >> 8); + array[pos + 3] = (byte) (value); + } + + /** + * + * + * @param str + * + * @throws IOException + */ + public void writeString(String str) throws IOException { + if (str == null) { + writeInt(0); + } else { + /* + writeInt(str.length()); + // don't use US-ASCII by default! + write(str.getBytes()); + */ + // patch as of version 0.2.9 + // for UTF-8 length of string is not necessarily + // equal to number of bytes + byte[] strBytes = str.getBytes(); + writeInt(strBytes.length); + write(strBytes); + } + } +} diff --git a/src/com/sshtools/j2ssh/io/DynamicBuffer.java b/src/com/sshtools/j2ssh/io/DynamicBuffer.java new file mode 100644 index 0000000000000000000000000000000000000000..307016d5abaca1011b875b41624eb5216bb700ff --- /dev/null +++ b/src/com/sshtools/j2ssh/io/DynamicBuffer.java @@ -0,0 +1,295 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; + + +/** + * <p> + * This class provides an alternative method of storing data, used within the + * API where Piped Streams could have been used. We found that Piped streams + * would lock if a thread attempted to read to data when the OutputStream attached + * was not being read; since we have no control over when the user will actually + * read the data, this behaviour led us to develop this dynamic buffer which + * will automatically grow if the buffer is full. + * </p> + * * + * @author Lee David Painter + * @version $Revision: 1.20 $ + */ +public class DynamicBuffer { + private static Log log = LogFactory.getLog(DynamicBuffer.class); + + /** Buffer size when the dynamic buffer is opened */ + protected static final int DEFAULT_BUFFER_SIZE = 32768; + + /** The buffer */ + protected byte[] buf; + + /** The current write position */ + protected int writepos = 0; + + /** The current read position */ + protected int readpos = 0; + + /** This buffers InputStream */ + protected InputStream in; + + /** This buffers OutputStream */ + protected OutputStream out; + private boolean closed = false; + private int interrupt = 5000; + + /** + * Creates a new DynamicBuffer object. + */ + public DynamicBuffer() { + buf = new byte[DEFAULT_BUFFER_SIZE]; + in = new DynamicBufferInputStream(); + out = new DynamicBufferOutputStream(); + } + + /** + * Get the InputStream of this buffer. Use the stream to read data from + * this buffer. + * + * @return + */ + public InputStream getInputStream() { + return in; + } + + /** + * Get the OutputStream of the buffer. Use this stream to write data to + * the buffer. + * + * @return + */ + public OutputStream getOutputStream() { + return out; + } + + private synchronized void verifyBufferSize(int count) { + // If there is not enough data in the buffer, then first attempt to + // move the unread data back to the beginning + if (count > (buf.length - writepos)) { + System.arraycopy(buf, readpos, buf, 0, writepos - readpos); + writepos -= readpos; + readpos = 0; + } + + // Now double check and increase the buffer size if necersary + if (count > (buf.length - writepos)) { + byte[] tmp = new byte[buf.length + DEFAULT_BUFFER_SIZE]; + System.arraycopy(buf, 0, tmp, 0, writepos - readpos); + buf = tmp; + } + } + + /** + * Return the number of bytes of data available to be read from the buffer + * @return + */ + protected synchronized int available() { + return writepos - readpos; + } + + private synchronized void block() throws InterruptedException { + if (log.isDebugEnabled()) { + log.debug("Buffer size: " + String.valueOf(buf.length)); + log.debug("Unread data: " + String.valueOf(writepos - readpos)); + } + + // Block and wait for more data + if (!closed) { + while ((readpos >= writepos) && !closed) { + wait(interrupt); + } + } + } + + /** + * Closes the buffer + */ + public synchronized void close() { + if (!closed) { + closed = true; + notifyAll(); + } + } + + /** + * Write a byte array to the buffer + * + * @param b + * + * @throws IOException + */ + protected synchronized void write(int b) throws IOException { + if (closed) { + throw new IOException("The buffer is closed"); + } + + verifyBufferSize(1); + buf[writepos] = (byte) b; + writepos++; + notifyAll(); + } + + /** + * + * + * @param data + * @param offset + * @param len + * + * @throws IOException + */ + protected synchronized void write(byte[] data, int offset, int len) + throws IOException { + if (closed) { + throw new IOException("The buffer is closed"); + } + + verifyBufferSize(len); + System.arraycopy(data, offset, buf, writepos, len); + writepos += len; + notifyAll(); + } + + public void setBlockInterrupt(int interrupt) { + this.interrupt = interrupt; + } + + /** + * Read a byte from the buffer + * + * @return + * + * @throws IOException + * @throws InterruptedIOException + */ + protected synchronized int read() throws IOException { + try { + block(); + } catch (InterruptedException ex) { + throw new InterruptedIOException( + "The blocking operation was interrupted"); + } + + if (closed && (available() <= 0)) { + return -1; + } + + return (int) buf[readpos++]; + } + + /** + * Read a byte array from the buffer + * + * @param data + * @param offset + * @param len + * + * @return + * + * @throws IOException + * @throws InterruptedIOException + */ + protected synchronized int read(byte[] data, int offset, int len) + throws IOException { + try { + block(); + } catch (InterruptedException ex) { + throw new InterruptedIOException( + "The blocking operation was interrupted"); + } + + if (closed && (available() <= 0)) { + return -1; + } + + int read = (len > (writepos - readpos)) ? (writepos - readpos) : len; + System.arraycopy(buf, readpos, data, offset, read); + readpos += read; + + return read; + } + + /** + * Flush data + * + * @throws IOException + */ + protected synchronized void flush() throws IOException { + notifyAll(); + } + + class DynamicBufferInputStream extends InputStream { + public int read() throws IOException { + return DynamicBuffer.this.read(); + } + + public int read(byte[] data, int offset, int len) + throws IOException { + return DynamicBuffer.this.read(data, offset, len); + } + + public int available() { + return DynamicBuffer.this.available(); + } + + public void close() { + DynamicBuffer.this.close(); + } + } + + class DynamicBufferOutputStream extends OutputStream { + public void write(int b) throws IOException { + DynamicBuffer.this.write(b); + } + + public void write(byte[] data, int offset, int len) + throws IOException { + DynamicBuffer.this.write(data, offset, len); + } + + public void flush() throws IOException { + DynamicBuffer.this.flush(); + } + + public void close() { + DynamicBuffer.this.close(); + } + } +} diff --git a/src/com/sshtools/j2ssh/io/IOStreamConnector.java b/src/com/sshtools/j2ssh/io/IOStreamConnector.java new file mode 100644 index 0000000000000000000000000000000000000000..e33800808875e0e59f067915169c6b5e379621e7 --- /dev/null +++ b/src/com/sshtools/j2ssh/io/IOStreamConnector.java @@ -0,0 +1,236 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import com.sshtools.j2ssh.*; + +import org.apache.commons.logging.*; + +import java.io.*; + +import javax.swing.event.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.29 $ + */ +public class IOStreamConnector { + private static Log log = LogFactory.getLog(IOStreamConnector.class); + private IOStreamConnectorState state = new IOStreamConnectorState(); + private InputStream in = null; + private OutputStream out = null; + private Thread thread; + private long bytes; + private boolean closeInput = true; + private boolean closeOutput = true; + + /** */ + protected EventListenerList listenerList = new EventListenerList(); + + /** + * Creates a new IOStreamConnector object. + */ + public IOStreamConnector() { + } + + /** + * Creates a new IOStreamConnector object. + * + * @param in + * @param out + */ + public IOStreamConnector(InputStream in, OutputStream out) { + connect(in, out); + } + + /** + * + * + * @return + */ + public IOStreamConnectorState getState() { + return state; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + log.info("Closing IOStreamConnector"); + state.setValue(IOStreamConnectorState.CLOSED); + + if (closeInput) { + in.close(); + } + + if (closeOutput) { + out.close(); + } + + thread = null; + } + + /** + * + * + * @param closeInput + */ + public void setCloseInput(boolean closeInput) { + this.closeInput = closeInput; + } + + /** + * + * + * @param closeOutput + */ + public void setCloseOutput(boolean closeOutput) { + this.closeOutput = closeOutput; + } + + /** + * + * + * @param in + * @param out + */ + public void connect(InputStream in, OutputStream out) { + this.in = in; + this.out = out; + log.info("Connecting InputStream to OutputStream"); + state.setValue(IOStreamConnectorState.CONNECTED); + thread = new SshThread(new IOStreamConnectorThread(), + "IOStream connector", true); + thread.start(); + } + + /** + * + * + * @return + */ + public long getBytes() { + return bytes; + } + + /** + * + * + * @param l + */ + public void addIOStreamConnectorListener(IOStreamConnectorListener l) { + listenerList.add(IOStreamConnectorListener.class, l); + } + + /** + * + * + * @param l + */ + public void removeIOStreamConnectorListener(IOStreamConnectorListener l) { + listenerList.remove(IOStreamConnectorListener.class, l); + } + + class IOStreamConnectorThread implements Runnable { + private Log log = LogFactory.getLog(IOStreamConnectorThread.class); + + public void run() { + byte[] buffer = new byte[4096]; + int read = 0; + int count; + int available; + log.info("Starting IOStreamConnectorThread thread"); + + while (state.getValue() == IOStreamConnectorState.CONNECTED) { + try { + // Block + read = in.read(buffer, 0, 1); + + if (read > 0) { + count = read; + available = in.available(); + + // Verify the buffer length and adjust if necersary + if ((available > 0) && + ((buffer.length - 1) < available)) { + byte[] tmp = new byte[available + 1]; + System.arraycopy(buffer, 0, tmp, 0, 1); + buffer = tmp; + } + + // Read the remaining available bytes of the message + if (available > 0) { + read = in.read(buffer, 1, available); + count += read; + } + + // Write the message to the output stream + out.write(buffer, 0, count); + bytes += count; + + // Flush it + out.flush(); + + // Inform all of the listeners + IOStreamConnectorListener[] l = (IOStreamConnectorListener[]) listenerList.getListeners(IOStreamConnectorListener.class); + + for (int i = (l.length - 1); i >= 0; i--) { + l[i].data(buffer, count); + } + } else { + log.debug("Blocking read returned with " + + String.valueOf(read)); + + if (read < 0) { + state.setValue(IOStreamConnectorState.EOF); + } + } + } catch (IOException ioe) { + // only warn if were supposed to be still connected, as we will ignore close exceptions + if (state.getValue() == IOStreamConnectorState.CONNECTED) { + log.debug(ioe.getMessage()); + state.setValue(IOStreamConnectorState.EOF); + } + } + } + + try { + // if were not already closed then close the connector + if (state.getValue() != IOStreamConnectorState.CLOSED) { + close(); + } + } catch (IOException ioe) { + } + + log.info("IOStreamConnectorThread is exiting"); + } + } +} diff --git a/src/com/sshtools/j2ssh/io/IOStreamConnectorListener.java b/src/com/sshtools/j2ssh/io/IOStreamConnectorListener.java new file mode 100644 index 0000000000000000000000000000000000000000..cb707eca20503422dab2a1af5fddc1b45da466b0 --- /dev/null +++ b/src/com/sshtools/j2ssh/io/IOStreamConnectorListener.java @@ -0,0 +1,45 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface IOStreamConnectorListener extends EventListener { + /** + * + * + * @param data + * @param count + */ + public void data(byte[] data, int count); +} diff --git a/src/com/sshtools/j2ssh/io/IOStreamConnectorState.java b/src/com/sshtools/j2ssh/io/IOStreamConnectorState.java new file mode 100644 index 0000000000000000000000000000000000000000..30052b4f6fc66aa554af773ab677429714de6ad1 --- /dev/null +++ b/src/com/sshtools/j2ssh/io/IOStreamConnectorState.java @@ -0,0 +1,68 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import com.sshtools.j2ssh.util.State; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class IOStreamConnectorState extends State { + /** */ + public final static int BOF = 1; + + /** */ + public final static int CONNECTED = 2; + + /** */ + public final static int EOF = 3; + + /** */ + public final static int CLOSED = 4; + + /** + * Creates a new IOStreamConnectorState object. + */ + public IOStreamConnectorState() { + super(BOF); + } + + /** + * + * + * @param state + * + * @return + */ + public boolean isValidState(int state) { + return ((state == BOF) || (state == CONNECTED) || (state == EOF) || + (state == CLOSED)); + } +} diff --git a/src/com/sshtools/j2ssh/io/IOUtil.java b/src/com/sshtools/j2ssh/io/IOUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..adeb1cbbb2da2d24d1271feb585c1ec7f08f989d --- /dev/null +++ b/src/com/sshtools/j2ssh/io/IOUtil.java @@ -0,0 +1,181 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class IOUtil { + /** + * + * + * @param in + * + * @return + */ + public static boolean closeStream(InputStream in) { + try { + if (in != null) { + in.close(); + } + + return true; + } catch (IOException ioe) { + return false; + } + } + + /** + * + * + * @param out + * + * @return + */ + public static boolean closeStream(OutputStream out) { + try { + if (out != null) { + out.close(); + } + + return true; + } catch (IOException ioe) { + return false; + } + } + + public static boolean delTree(File file) { + if (file.isFile()) { + return file.delete(); + } else { + File[] list = file.listFiles(); + + for (int i = 0; i < list.length; i++) { + if (!delTree(list[i])) { + return false; + } + } + } + + return true; + } + + public static void recurseDeleteDirectory(File dir) { + File[] files = dir.listFiles(new FileFilter() { + public boolean accept(File file) { + return file.isDirectory(); + } + }); + + if (files == null) { + return; // Directory could not be read + } + + for (int i = 0; i < files.length; i++) { + recurseDeleteDirectory(files[i]); + files[i].delete(); + } + + files = dir.listFiles(new FileFilter() { + public boolean accept(File file) { + return !file.isDirectory(); + } + }); + + for (int i = 0; i < files.length; i++) { + files[i].delete(); + } + + dir.delete(); + } + + public static void copyFile(File from, File to) throws IOException { + if (from.isDirectory()) { + if (!to.exists()) { + to.mkdir(); + } + + File[] children = from.listFiles(); + + for (int i = 0; i < children.length; i++) { + if (children[i].getName().equals(".") || + children[i].getName().equals("..")) { + continue; + } + + if (children[i].isDirectory()) { + File f = new File(to, children[i].getName()); + copyFile(children[i], f); + } else { + copyFile(children[i], to); + } + } + } else if (from.isFile() && (to.isDirectory() || to.isFile())) { + if (to.isDirectory()) { + to = new File(to, from.getName()); + } + + FileInputStream in = new FileInputStream(from); + FileOutputStream out = new FileOutputStream(to); + byte[] buf = new byte[32678]; + int read; + + while ((read = in.read(buf)) > -1) { + out.write(buf, 0, read); + } + + closeStream(in); + closeStream(out); + } + } + + public static void transfer(InputStream in, OutputStream out) + throws IOException { + try { + long bytesSoFar = 0; + byte[] buffer = new byte[65535]; + int read; + + while ((read = in.read(buffer)) > -1) { + if (read > 0) { + out.write(buffer, 0, read); + + //out.flush(); + bytesSoFar += read; + } + } + } finally { + closeStream(in); + closeStream(out); + } + } +} diff --git a/src/com/sshtools/j2ssh/io/UnsignedInteger32.java b/src/com/sshtools/j2ssh/io/UnsignedInteger32.java new file mode 100644 index 0000000000000000000000000000000000000000..02f635545b204f22230c868bc1e2b9223633b4af --- /dev/null +++ b/src/com/sshtools/j2ssh/io/UnsignedInteger32.java @@ -0,0 +1,190 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class UnsignedInteger32 extends Number implements Serializable { + final static long serialVersionUID = 200; + + /** */ + public final static long MAX_VALUE = 0xffffffffL; + + /** */ + public final static long MIN_VALUE = 0; + private Long value; + + /** + * Creates a new UnsignedInteger32 object. + * + * @param a + * + * @throws NumberFormatException + */ + public UnsignedInteger32(long a) { + if ((a < MIN_VALUE) || (a > MAX_VALUE)) { + throw new NumberFormatException(); + } + + value = new Long(a); + } + + /** + * Creates a new UnsignedInteger32 object. + * + * @param a + * + * @throws NumberFormatException + */ + public UnsignedInteger32(String a) throws NumberFormatException { + Long temp = new Long(a); + long longValue = temp.longValue(); + + if ((longValue < MIN_VALUE) || (longValue > MAX_VALUE)) { + throw new NumberFormatException(); + } + + value = new Long(longValue); + } + + /** + * + * + * @return + */ + public byte byteValue() { + return value.byteValue(); + } + + /** + * + * + * @return + */ + public short shortValue() { + return value.shortValue(); + } + + /** + * + * + * @return + */ + public int intValue() { + return value.intValue(); + } + + /** + * + * + * @return + */ + public long longValue() { + return value.longValue(); + } + + /** + * + * + * @return + */ + public float floatValue() { + return value.floatValue(); + } + + /** + * + * + * @return + */ + public double doubleValue() { + return value.doubleValue(); + } + + /** + * + * + * @return + */ + public String toString() { + return value.toString(); + } + + /** + * + * + * @return + */ + public int hashCode() { + return value.hashCode(); + } + + /** + * + * + * @param o + * + * @return + */ + public boolean equals(Object o) { + if (!(o instanceof UnsignedInteger32)) { + return false; + } + + return (((UnsignedInteger32) o).value.equals(this.value)); + } + + /** + * + * + * @param x + * @param y + * + * @return + */ + public static UnsignedInteger32 add(UnsignedInteger32 x, UnsignedInteger32 y) { + return new UnsignedInteger32(x.longValue() + y.longValue()); + } + + /** + * + * + * @param x + * @param y + * + * @return + */ + public static UnsignedInteger32 add(UnsignedInteger32 x, int y) { + return new UnsignedInteger32(x.longValue() + y); + } +} diff --git a/src/com/sshtools/j2ssh/io/UnsignedInteger64.java b/src/com/sshtools/j2ssh/io/UnsignedInteger64.java new file mode 100644 index 0000000000000000000000000000000000000000..14942249706f784aa0a7ebdad17660da53ee7b00 --- /dev/null +++ b/src/com/sshtools/j2ssh/io/UnsignedInteger64.java @@ -0,0 +1,214 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.io; + +import java.io.*; + +import java.math.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class UnsignedInteger64 extends Number implements Serializable, + Comparable { + final static long serialVersionUID = 200; + + /** */ + public final static BigInteger MAX_VALUE = new BigInteger( + "18446744073709551615"); + + /** */ + public final static BigInteger MIN_VALUE = new BigInteger("0"); + private BigInteger bigInt; + + /** + * Creates a new UnsignedInteger64 object. + * + * @param sval + * + * @throws NumberFormatException + */ + public UnsignedInteger64(String sval) throws NumberFormatException { + bigInt = new BigInteger(sval); + + if ((bigInt.compareTo(MIN_VALUE) < 0) || + (bigInt.compareTo(MAX_VALUE) > 0)) { + throw new NumberFormatException(); + } + } + + /** + * Creates a new UnsignedInteger64 object. + * + * @param bval + * + * @throws NumberFormatException + */ + public UnsignedInteger64(byte[] bval) throws NumberFormatException { + bigInt = new BigInteger(bval); + + if ((bigInt.compareTo(MIN_VALUE) < 0) || + (bigInt.compareTo(MAX_VALUE) > 0)) { + throw new NumberFormatException(); + } + } + + /** + * Creates a new UnsignedInteger64 object. + * + * @param input + * + * @throws NumberFormatException + */ + public UnsignedInteger64(BigInteger input) { + bigInt = new BigInteger(input.toString()); + + if ((bigInt.compareTo(MIN_VALUE) < 0) || + (bigInt.compareTo(MAX_VALUE) > 0)) { + throw new NumberFormatException(); + } + } + + /** + * + * + * @param o + * + * @return + */ + public boolean equals(Object o) { + try { + UnsignedInteger64 u = (UnsignedInteger64) o; + + return u.bigInt.equals(this.bigInt); + } catch (ClassCastException ce) { + // This was not an UnsignedInt64, so equals should fail. + return false; + } + } + + /** + * + * + * @return + */ + public BigInteger bigIntValue() { + return bigInt; + } + + /** + * + * + * @return + */ + public int intValue() { + return bigInt.intValue(); + } + + /** + * + * + * @return + */ + public long longValue() { + return bigInt.longValue(); + } + + /** + * + * + * @return + */ + public double doubleValue() { + return bigInt.doubleValue(); + } + + /** + * + * + * @return + */ + public float floatValue() { + return bigInt.floatValue(); + } + + /** + * + * + * @param val + * + * @return + */ + public int compareTo(Object val) { + return bigInt.compareTo(((UnsignedInteger64) val).bigInt); + } + + /** + * + * + * @return + */ + public String toString() { + return bigInt.toString(); + } + + /** + * + * + * @return + */ + public int hashCode() { + return bigInt.hashCode(); + } + + /** + * + * + * @param x + * @param y + * + * @return + */ + public static UnsignedInteger64 add(UnsignedInteger64 x, UnsignedInteger64 y) { + return new UnsignedInteger64(x.bigInt.add(y.bigInt)); + } + + /** + * + * + * @param x + * @param y + * + * @return + */ + public static UnsignedInteger64 add(UnsignedInteger64 x, int y) { + return new UnsignedInteger64(x.bigInt.add(BigInteger.valueOf(y))); + } +} diff --git a/src/com/sshtools/j2ssh/net/ConnectedSocketTransportProvider.java b/src/com/sshtools/j2ssh/net/ConnectedSocketTransportProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..9e75e29ba6f7dcddb19c7d2be555ee3b97d4f1e1 --- /dev/null +++ b/src/com/sshtools/j2ssh/net/ConnectedSocketTransportProvider.java @@ -0,0 +1,88 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.Socket; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class ConnectedSocketTransportProvider implements TransportProvider { + Socket socket; + + /** + * Creates a new ConnectedSocketTransportProvider object. + * + * @param socket + */ + public ConnectedSocketTransportProvider(Socket socket) { + this.socket = socket; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + socket.close(); + } + + /*public boolean isConnected() { + return true; //socket.isConnected(); + }*/ + public InputStream getInputStream() throws IOException { + return socket.getInputStream(); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public OutputStream getOutputStream() throws IOException { + return socket.getOutputStream(); + } + + /** + * + * + * @return + */ + public String getProviderDetail() { + return socket.toString(); //getRemoteSocketAddress().toString(); + } +} diff --git a/src/com/sshtools/j2ssh/net/HttpHeader.java b/src/com/sshtools/j2ssh/net/HttpHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..901ff9b2f363a9023c9a7ebbc1851fd39efdd9b2 --- /dev/null +++ b/src/com/sshtools/j2ssh/net/HttpHeader.java @@ -0,0 +1,223 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public abstract class HttpHeader { + /** */ + protected final static String white_SPACE = " \t\r"; + HashMap fields; + + /** */ + protected String begin; + + /** + * Creates a new HttpHeader object. + */ + protected HttpHeader() { + fields = new HashMap(); + } + + /** + * + * + * @param in + * + * @return + * + * @throws IOException + */ + protected String readLine(InputStream in) throws IOException { + StringBuffer lineBuf = new StringBuffer(); + int c; + + while (true) { + c = in.read(); + + if (c == -1) { + throw new IOException( + "Failed to read expected HTTP header line"); + } + + if (c == '\n') { + continue; + } + + if (c != '\r') { + lineBuf.append((char) c); + } else { + break; + } + } + + return new String(lineBuf); + } + + /** + * + * + * @return + */ + public String getStartLine() { + return begin; + } + + /** + * + * + * @return + */ + public Map getHeaderFields() { + return fields; + } + + /** + * + * + * @return + */ + public Set getHeaderFieldNames() { + return fields.keySet(); + } + + /** + * + * + * @param headerName + * + * @return + */ + public String getHeaderField(String headerName) { + return (String) fields.get(headerName.toLowerCase()); + } + + /** + * + * + * @param headerName + * @param value + */ + public void setHeaderField(String headerName, String value) { + fields.put(headerName.toLowerCase(), value); + } + + /** + * + * + * @return + */ + public String toString() { + String str = begin + "\r\n"; + Iterator it = getHeaderFieldNames().iterator(); + + while (it.hasNext()) { + String fieldName = (String) it.next(); + str += (fieldName + ": " + getHeaderField(fieldName) + "\r\n"); + } + + str += "\r\n"; + + return str; + } + + /** + * + * + * @param in + * + * @throws IOException + */ + protected void processHeaderFields(InputStream in) + throws IOException { + fields = new HashMap(); + + StringBuffer lineBuf = new StringBuffer(); + String lastHeaderName = null; + int c; + + while (true) { + c = in.read(); + + if (c == -1) { + throw new IOException("The HTTP header is corrupt"); + } + + if (c == '\n') { + continue; + } + + if (c != '\r') { + lineBuf.append((char) c); + } else { + if (lineBuf.length() != 0) { + String line = lineBuf.toString(); + lastHeaderName = processNextLine(line, lastHeaderName); + lineBuf.setLength(0); + } else { + break; + } + } + } + + c = in.read(); + } + + private String processNextLine(String line, String lastHeaderName) + throws IOException { + String name; + String value; + char c = line.charAt(0); + + if ((c == ' ') || (c == '\t')) { + name = lastHeaderName; + value = getHeaderField(lastHeaderName) + " " + line.trim(); + } else { + int n = line.indexOf(':'); + + if (n == -1) { + throw new IOException( + "HTTP Header encoutered a corrupt field: '" + line + "'"); + } + + name = line.substring(0, n).toLowerCase(); + value = line.substring(n + 1).trim(); + } + + setHeaderField(name, value); + + return name; + } +} diff --git a/src/com/sshtools/j2ssh/net/HttpProxySocketProvider.java b/src/com/sshtools/j2ssh/net/HttpProxySocketProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..5f1c512dbca4015ff295308b667ac03610c5621e --- /dev/null +++ b/src/com/sshtools/j2ssh/net/HttpProxySocketProvider.java @@ -0,0 +1,205 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class HttpProxySocketProvider extends Socket implements TransportProvider { + private String proxyHost; + private int proxyPort; + private String remoteHost; + private int remotePort; + private HttpResponse responseHeader; + private String providerDetail; + + private HttpProxySocketProvider(String host, int port, String proxyHost, + int proxyPort) throws IOException, UnknownHostException { + super(proxyHost, proxyPort); + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.remoteHost = remoteHost; + this.remotePort = remotePort; + } + + /** + * + * + * @param host + * @param port + * @param proxyHost + * @param proxyPort + * @param username + * @param password + * @param userAgent + * + * @return + * + * @throws IOException + * @throws UnknownHostException + */ + public static HttpProxySocketProvider connectViaProxy(String host, + int port, String proxyHost, int proxyPort, String username, + String password, String userAgent) + throws IOException, UnknownHostException { + return connectViaProxy(host, port, proxyHost, proxyPort, null, + username, password, userAgent); + } + + /** + * + * + * @param host + * @param port + * @param proxyHost + * @param proxyPort + * @param protocol + * @param username + * @param password + * @param userAgent + * + * @return + * + * @throws IOException + * @throws UnknownHostException + * @throws SocketException + */ + public static HttpProxySocketProvider connectViaProxy(String host, + int port, String proxyHost, int proxyPort, String protocol, + String username, String password, String userAgent) + throws IOException, UnknownHostException { + HttpProxySocketProvider socket = new HttpProxySocketProvider(host, + port, proxyHost, proxyPort); + int status; + String providerDetail; + + try { + InputStream in = socket.getInputStream(); + OutputStream out = socket.getOutputStream(); + HttpRequest request = new HttpRequest(); + + if (protocol == null) { + protocol = ""; + } + + request.setHeaderBegin("CONNECT " + protocol + host + ":" + port + + " HTTP/1.0"); + request.setHeaderField("User-Agent", userAgent); + request.setHeaderField("Pragma", "No-Cache"); + request.setHeaderField("Proxy-Connection", "Keep-Alive"); + out.write(request.toString().getBytes()); + out.flush(); + socket.responseHeader = new HttpResponse(in); + providerDetail = socket.responseHeader.getHeaderField("server"); + + if (socket.responseHeader.getStatus() == 407) { + String realm = socket.responseHeader.getAuthenticationRealm(); + String method = socket.responseHeader.getAuthenticationMethod(); + + if (realm == null) { + realm = ""; + } + + if (method.equalsIgnoreCase("basic")) { + socket.close(); + socket = new HttpProxySocketProvider(host, port, proxyHost, + proxyPort); + in = socket.getInputStream(); + out = socket.getOutputStream(); + request.setBasicAuthentication(username, password); + out.write(request.toString().getBytes()); + out.flush(); + socket.responseHeader = new HttpResponse(in); + } else if (method.equalsIgnoreCase("digest")) { + throw new IOException( + "Digest authentication is not supported"); + } else { + throw new IOException("'" + method + "' is not supported"); + } + } + + status = socket.responseHeader.getStatus(); + } catch (SocketException e) { + throw new SocketException("Error communicating with proxy server " + + proxyHost + ":" + proxyPort + " (" + e.getMessage() + ")"); + } + + if ((status < 200) || (status > 299)) { + throw new IOException("Proxy tunnel setup failed: " + + socket.responseHeader.getStartLine()); + } + + socket.providerDetail = providerDetail; + + return socket; + } + + /** + * + * + * @return + */ + public String toString() { + return "HTTPProxySocket [Proxy IP=" + getInetAddress() + + ",Proxy Port=" + getPort() + ",localport=" + getLocalPort() + + "Remote Host=" + remoteHost + "Remote Port=" + + String.valueOf(remotePort) + "]"; + } + + /** + * + * + * @return + */ + public HttpHeader getResponseHeader() { + return responseHeader; + } + + /** + * + * + * @return + */ + public String getProviderDetail() { + return providerDetail; + } + + /*public boolean isConnected() { + return true; + }*/ +} diff --git a/src/com/sshtools/j2ssh/net/HttpRequest.java b/src/com/sshtools/j2ssh/net/HttpRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..4dc0d80ef899d81cea5c24519ff64d7767bf14e9 --- /dev/null +++ b/src/com/sshtools/j2ssh/net/HttpRequest.java @@ -0,0 +1,65 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import com.sshtools.j2ssh.util.Base64; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class HttpRequest extends HttpHeader { + /** + * Creates a new HttpRequest object. + */ + public HttpRequest() { + super(); + } + + /** + * + * + * @param begin + */ + public void setHeaderBegin(String begin) { + this.begin = begin; + } + + /** + * + * + * @param username + * @param password + */ + public void setBasicAuthentication(String username, String password) { + String str = username + ":" + password; + setHeaderField("Proxy-Authorization", + "Basic " + Base64.encodeBytes(str.getBytes(), true)); + } +} diff --git a/src/com/sshtools/j2ssh/net/HttpResponse.java b/src/com/sshtools/j2ssh/net/HttpResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..c3397ed7ea4f982fa3511b84ff5bd843f8353773 --- /dev/null +++ b/src/com/sshtools/j2ssh/net/HttpResponse.java @@ -0,0 +1,151 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import java.io.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class HttpResponse extends HttpHeader { + private String version; + private int status; + private String reason; + + /** + * Creates a new HttpResponse object. + * + * @param input + * + * @throws IOException + */ + public HttpResponse(InputStream input) throws IOException { + begin = readLine(input); + + while (begin.trim().length() == 0) { + begin = readLine(input); + } + + processResponse(); + processHeaderFields(input); + } + + /** + * + * + * @return + */ + public String getVersion() { + return version; + } + + /** + * + * + * @return + */ + public int getStatus() { + return status; + } + + /** + * + * + * @return + */ + public String getReason() { + return reason; + } + + private void processResponse() throws IOException { + StringTokenizer tokens = new StringTokenizer(begin, white_SPACE, false); + + try { + version = tokens.nextToken(); + status = Integer.parseInt(tokens.nextToken()); + reason = tokens.nextToken(); + } catch (NoSuchElementException e) { + throw new IOException("Failed to read HTTP repsonse header"); + } catch (NumberFormatException e) { + throw new IOException("Failed to read HTTP resposne header"); + } + } + + /** + * + * + * @return + */ + public String getAuthenticationMethod() { + String auth = getHeaderField("Proxy-Authenticate"); + String method = null; + + if (auth != null) { + int n = auth.indexOf(' '); + method = auth.substring(0, n); + } + + return method; + } + + /** + * + * + * @return + */ + public String getAuthenticationRealm() { + String auth = getHeaderField("Proxy-Authenticate"); + String realm = null; + + if (auth != null) { + int l; + int r = auth.indexOf('='); + + while (r >= 0) { + l = auth.lastIndexOf(' ', r); + realm = auth.substring(l + 1, r); + + if (realm.equalsIgnoreCase("realm")) { + l = r + 2; + r = auth.indexOf('"', l); + realm = auth.substring(l, r); + + break; + } + + r = auth.indexOf('=', r + 1); + } + } + + return realm; + } +} diff --git a/src/com/sshtools/j2ssh/net/SocketTransportProvider.java b/src/com/sshtools/j2ssh/net/SocketTransportProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..034ded634bba3e3a3d70624418d2a7f057ffaac4 --- /dev/null +++ b/src/com/sshtools/j2ssh/net/SocketTransportProvider.java @@ -0,0 +1,65 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import java.io.IOException; + +import java.net.Socket; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class SocketTransportProvider extends Socket implements TransportProvider { + /** + * Creates a new SocketTransportProvider object. + * + * @param host + * @param port + * + * @throws IOException + */ + protected SocketTransportProvider(String host, int port) + throws IOException { + super(host, port); + } + + /** + * + * + * @return + */ + public String getProviderDetail() { + return toString(); //getRemoteSocketAddress().toString(); + } + + /*public boolean isConnected() { + return true; + }*/ +} diff --git a/src/com/sshtools/j2ssh/net/SocksProxySocket.java b/src/com/sshtools/j2ssh/net/SocksProxySocket.java new file mode 100644 index 0000000000000000000000000000000000000000..79c01b6e872c832c168c67106dd9e84144d394b2 --- /dev/null +++ b/src/com/sshtools/j2ssh/net/SocksProxySocket.java @@ -0,0 +1,377 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class SocksProxySocket extends Socket implements TransportProvider { + /** */ + public static final int SOCKS4 = 0x04; + + /** */ + public static final int SOCKS5 = 0x05; + private static final int CONNECT = 0x01; + private static final int NULL_TERMINATION = 0x00; + private final static String[] SOCKSV5_ERROR = { + "Success", "General SOCKS server failure", + "Connection not allowed by ruleset", "Network unreachable", + "Host unreachable", "Connection refused", "TTL expired", + "Command not supported", "Address type not supported" + }; + private final static String[] SOCKSV4_ERROR = { + "Request rejected or failed", + "SOCKS server cannot connect to identd on the client", + "The client program and identd report different user-ids" + }; + private String proxyHost; + private int proxyPort; + private String remoteHost; + private int remotePort; + private String providerDetail; + + private SocksProxySocket(String remoteHost, int remotePort, + String proxyHost, int proxyPort) + throws IOException, UnknownHostException { + super(proxyHost, proxyPort); + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.remoteHost = remoteHost; + this.remotePort = remotePort; + } + + /** + * + * + * @param remoteHost + * @param remotePort + * @param proxyHost + * @param proxyPort + * @param userId + * + * @return + * + * @throws IOException + * @throws UnknownHostException + * @throws SocketException + */ + public static SocksProxySocket connectViaSocks4Proxy(String remoteHost, + int remotePort, String proxyHost, int proxyPort, String userId) + throws IOException, UnknownHostException { + SocksProxySocket proxySocket = new SocksProxySocket(remoteHost, + remotePort, proxyHost, proxyPort); + + try { + InputStream proxyIn = proxySocket.getInputStream(); + OutputStream proxyOut = proxySocket.getOutputStream(); + InetAddress hostAddr = InetAddress.getByName(remoteHost); + proxyOut.write(SOCKS4); + proxyOut.write(CONNECT); + proxyOut.write((remotePort >>> 8) & 0xff); + proxyOut.write(remotePort & 0xff); + proxyOut.write(hostAddr.getAddress()); + proxyOut.write(userId.getBytes()); + proxyOut.write(NULL_TERMINATION); + proxyOut.flush(); + + int res = proxyIn.read(); + + if (res == -1) { + throw new IOException("SOCKS4 server " + proxyHost + ":" + + proxyPort + " disconnected"); + } + + if (res != 0x00) { + throw new IOException("Invalid response from SOCKS4 server (" + + res + ") " + proxyHost + ":" + proxyPort); + } + + int code = proxyIn.read(); + + if (code != 90) { + if ((code > 90) && (code < 93)) { + throw new IOException( + "SOCKS4 server unable to connect, reason: " + + SOCKSV4_ERROR[code - 91]); + } else { + throw new IOException( + "SOCKS4 server unable to connect, reason: " + code); + } + } + + byte[] data = new byte[6]; + + if (proxyIn.read(data, 0, 6) != 6) { + throw new IOException( + "SOCKS4 error reading destination address/port"); + } + + proxySocket.providerDetail = data[2] + "." + data[3] + "." + + data[4] + "." + data[5] + ":" + ((data[0] << 8) | data[1]); + } catch (SocketException e) { + throw new SocketException("Error communicating with SOCKS4 server " + + proxyHost + ":" + proxyPort + ", " + e.getMessage()); + } + + return proxySocket; + } + + /** + * + * + * @param remoteHost + * @param remotePort + * @param proxyHost + * @param proxyPort + * @param localLookup + * @param username + * @param password + * + * @return + * + * @throws IOException + * @throws UnknownHostException + * @throws SocketException + */ + public static SocksProxySocket connectViaSocks5Proxy(String remoteHost, + int remotePort, String proxyHost, int proxyPort, boolean localLookup, + String username, String password) + throws IOException, UnknownHostException { + SocksProxySocket proxySocket = new SocksProxySocket(remoteHost, + remotePort, proxyHost, proxyPort); + + try { + InputStream proxyIn = proxySocket.getInputStream(); + OutputStream proxyOut = proxySocket.getOutputStream(); + byte[] request = { + (byte) SOCKS5, (byte) 0x02, (byte) 0x00, (byte) 0x02 + }; + byte[] reply = new byte[2]; + proxyOut.write(request); + proxyOut.flush(); + + int res = proxyIn.read(); + + if (res == -1) { + throw new IOException("SOCKS5 server " + proxyHost + ":" + + proxyPort + " disconnected"); + } + + if (res != 0x05) { + throw new IOException("Invalid response from SOCKS5 server (" + + res + ") " + proxyHost + ":" + proxyPort); + } + + int method = proxyIn.read(); + + switch (method) { + case 0x00: + break; + + case 0x02: + performAuthentication(proxyIn, proxyOut, username, password, + proxyHost, proxyPort); + + break; + + default: + throw new IOException( + "SOCKS5 server does not support our authentication methods"); + } + + if (localLookup) { + InetAddress hostAddr; + + try { + hostAddr = InetAddress.getByName(remoteHost); + } catch (UnknownHostException e) { + throw new IOException("Can't do local lookup on: " + + remoteHost + ", try socks5 without local lookup"); + } + + request = new byte[] { + (byte) SOCKS5, (byte) 0x01, (byte) 0x00, (byte) 0x01 + }; + proxyOut.write(request); + proxyOut.write(hostAddr.getAddress()); + } else { + request = new byte[] { + (byte) SOCKS5, (byte) 0x01, (byte) 0x00, (byte) 0x03 + }; + proxyOut.write(request); + proxyOut.write(remoteHost.length()); + proxyOut.write(remoteHost.getBytes()); + } + + proxyOut.write((remotePort >>> 8) & 0xff); + proxyOut.write(remotePort & 0xff); + proxyOut.flush(); + res = proxyIn.read(); + + if (res != 0x05) { + throw new IOException("Invalid response from SOCKS5 server (" + + res + ") " + proxyHost + ":" + proxyPort); + } + + int status = proxyIn.read(); + + if (status != 0x00) { + if ((status > 0) && (status < 9)) { + throw new IOException( + "SOCKS5 server unable to connect, reason: " + + SOCKSV5_ERROR[status]); + } else { + throw new IOException( + "SOCKS5 server unable to connect, reason: " + status); + } + } + + proxyIn.read(); + + int aType = proxyIn.read(); + byte[] data = new byte[255]; + + switch (aType) { + case 0x01: + + if (proxyIn.read(data, 0, 4) != 4) { + throw new IOException("SOCKS5 error reading address"); + } + + proxySocket.providerDetail = data[0] + "." + data[1] + "." + + data[2] + "." + data[3]; + + break; + + case 0x03: + + int n = proxyIn.read(); + + if (proxyIn.read(data, 0, n) != n) { + throw new IOException("SOCKS5 error reading address"); + } + + proxySocket.providerDetail = new String(data); + + break; + + default: + throw new IOException("SOCKS5 gave unsupported address type: " + + aType); + } + + if (proxyIn.read(data, 0, 2) != 2) { + throw new IOException("SOCKS5 error reading port"); + } + + proxySocket.providerDetail += (":" + ((data[0] << 8) | data[1])); + } catch (SocketException e) { + throw new SocketException("Error communicating with SOCKS5 server " + + proxyHost + ":" + proxyPort + ", " + e.getMessage()); + } + + return proxySocket; + } + + /** + * + * + * @return + */ + public String getProviderDetail() { + return providerDetail; + } + + private static void performAuthentication(InputStream proxyIn, + OutputStream proxyOut, String username, String password, + String proxyHost, int proxyPort) throws IOException { + proxyOut.write(0x01); + proxyOut.write(username.length()); + proxyOut.write(username.getBytes()); + proxyOut.write(password.length()); + proxyOut.write(password.getBytes()); + + int res = proxyIn.read(); + + if ((res != 0x01) && (res != 0x05)) { + throw new IOException("Invalid response from SOCKS5 server (" + + res + ") " + proxyHost + ":" + proxyPort); + } + + if (proxyIn.read() != 0x00) { + throw new IOException("Invalid username/password for SOCKS5 server"); + } + } + + /** + * + * + * @return + */ + public String toString() { + return "SocksProxySocket[addr=" + getInetAddress() + ",port=" + + getPort() + ",localport=" + getLocalPort() + "]"; + } + + /** + * + * + * @param remoteHost + * @param remotePort + * @param proxyHost + * @param proxyPort + * @param username + * @param password + * + * @return + * + * @throws IOException + * @throws UnknownHostException + */ + public static SocksProxySocket connectViaSocks5Proxy(String remoteHost, + int remotePort, String proxyHost, int proxyPort, String username, + String password) throws IOException, UnknownHostException { + return connectViaSocks5Proxy(remoteHost, remotePort, proxyHost, + proxyPort, false, username, password); + } + + /*public boolean isConnected() { + return true; + }*/ +} diff --git a/src/com/sshtools/j2ssh/net/TransportProvider.java b/src/com/sshtools/j2ssh/net/TransportProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..d47a95394fa5018659964f4c78874c0a28efeaaf --- /dev/null +++ b/src/com/sshtools/j2ssh/net/TransportProvider.java @@ -0,0 +1,63 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public interface TransportProvider { + /** + * + * + * @throws IOException + */ + public void close() throws IOException; + + //public boolean isConnected(); + public InputStream getInputStream() throws IOException; + + /** + * + * + * @return + * + * @throws IOException + */ + public OutputStream getOutputStream() throws IOException; + + /** + * + * + * @return + */ + public String getProviderDetail(); +} diff --git a/src/com/sshtools/j2ssh/net/TransportProviderFactory.java b/src/com/sshtools/j2ssh/net/TransportProviderFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..c5d3b85c7500ed7605692f1e35acbb73e04cf46e --- /dev/null +++ b/src/com/sshtools/j2ssh/net/TransportProviderFactory.java @@ -0,0 +1,86 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.net; + +import com.sshtools.j2ssh.configuration.SshConnectionProperties; + +import java.io.IOException; + +import java.net.UnknownHostException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class TransportProviderFactory { + /** + * + * + * @param properties + * @param socketTimeout + * + * @return + * + * @throws UnknownHostException + * @throws IOException + */ + public static TransportProvider connectTransportProvider( + SshConnectionProperties properties /*, int connectTimeout*/, + int socketTimeout) throws UnknownHostException, IOException { + if (properties.getTransportProvider() == SshConnectionProperties.USE_HTTP_PROXY) { + return HttpProxySocketProvider.connectViaProxy(properties.getHost(), + properties.getPort(), properties.getProxyHost(), + properties.getProxyPort(), properties.getProxyUsername(), + properties.getProxyPassword(), "J2SSH"); + } else if (properties.getTransportProvider() == SshConnectionProperties.USE_SOCKS4_PROXY) { + return SocksProxySocket.connectViaSocks4Proxy(properties.getHost(), + properties.getPort(), properties.getProxyHost(), + properties.getProxyPort(), properties.getProxyUsername()); + } else if (properties.getTransportProvider() == SshConnectionProperties.USE_SOCKS5_PROXY) { + return SocksProxySocket.connectViaSocks5Proxy(properties.getHost(), + properties.getPort(), properties.getProxyHost(), + properties.getProxyPort(), properties.getProxyUsername(), + properties.getProxyPassword()); + } else { + // No proxy just attempt a standard socket connection + + /*SocketTransportProvider socket = new SocketTransportProvider(); + socket.setSoTimeout(socketTimeout); + socket.connect(new InetSocketAddress(properties.getHost(), + properties.getPort()), + connectTimeout);*/ + SocketTransportProvider socket = new SocketTransportProvider(properties.getHost(), + properties.getPort()); + socket.setTcpNoDelay(true); + socket.setSoTimeout(socketTimeout); + + return socket; + } + } +} diff --git a/src/com/sshtools/j2ssh/openssh/DSAKeyInfo.java b/src/com/sshtools/j2ssh/openssh/DSAKeyInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..585cc4ae00566f04cf0635d44d1c14f15608e507 --- /dev/null +++ b/src/com/sshtools/j2ssh/openssh/DSAKeyInfo.java @@ -0,0 +1,202 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.openssh; + +import com.sshtools.j2ssh.util.SimpleASNReader; +import com.sshtools.j2ssh.util.SimpleASNWriter; + +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import java.security.spec.KeySpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class DSAKeyInfo implements KeyInfo { + private BigInteger p; + private BigInteger q; + private BigInteger g; + private BigInteger x; + private BigInteger y; + + /** + * Creates a new DSAKeyInfo object. + * + * @param p + * @param q + * @param g + * @param x + * @param y + */ + public DSAKeyInfo(BigInteger p, BigInteger q, BigInteger g, BigInteger x, + BigInteger y) { + this.p = p; + this.q = q; + this.g = g; + this.x = x; + this.y = y; + } + + /** + * + * + * @return + */ + public BigInteger getG() { + return g; + } + + /** + * + * + * @return + */ + public BigInteger getP() { + return p; + } + + /** + * + * + * @return + */ + public BigInteger getQ() { + return q; + } + + /** + * + * + * @return + */ + public BigInteger getX() { + return x; + } + + /** + * + * + * @return + */ + public BigInteger getY() { + return y; + } + + /** + * + * + * @return + */ + public KeySpec getPrivateKeySpec() { + return new DSAPrivateKeySpec(x, p, q, g); + } + + /** + * + * + * @return + */ + public KeySpec getPublicKeySpec() { + return new DSAPublicKeySpec(y, p, q, g); + } + + /** + * + * + * @param asn + * + * @return + * + * @throws IOException + */ + public static DSAKeyInfo getDSAKeyInfo(SimpleASNReader asn) + throws IOException { + asn.assertByte(0x30); // SEQUENCE + + int length = asn.getLength(); + asn.assertByte(0x02); // INTEGER (version) + + byte[] version = asn.getData(); + asn.assertByte(0x02); // INTEGER (p) + + byte[] paramP = asn.getData(); + asn.assertByte(0x02); // INTEGER (q) + + byte[] paramQ = asn.getData(); + asn.assertByte(0x02); // INTEGER (g) + + byte[] paramG = asn.getData(); + asn.assertByte(0x02); // INTEGER (y) + + byte[] paramY = asn.getData(); + asn.assertByte(0x02); // INTEGER (x) + + byte[] paramX = asn.getData(); + + return new DSAKeyInfo(new BigInteger(paramP), new BigInteger(paramQ), + new BigInteger(paramG), new BigInteger(paramX), + new BigInteger(paramY)); + } + + /** + * + * + * @param asn + * @param keyInfo + */ + public static void writeDSAKeyInfo(SimpleASNWriter asn, DSAKeyInfo keyInfo) { + // Write to a substream temporarily. + // This code needs to know the length of the substream before it can write the data from + // the substream to the main stream. + SimpleASNWriter asn2 = new SimpleASNWriter(); + asn2.writeByte(0x02); // INTEGER (version) + + byte[] version = new byte[1]; + asn2.writeData(version); + asn2.writeByte(0x02); // INTEGER (p) + asn2.writeData(keyInfo.getP().toByteArray()); + asn2.writeByte(0x02); // INTEGER (q) + asn2.writeData(keyInfo.getQ().toByteArray()); + asn2.writeByte(0x02); // INTEGER (g) + asn2.writeData(keyInfo.getG().toByteArray()); + asn2.writeByte(0x02); // INTEGER (y) + asn2.writeData(keyInfo.getY().toByteArray()); + asn2.writeByte(0x02); // INTEGER (x) + asn2.writeData(keyInfo.getX().toByteArray()); + + byte[] dsaKeyEncoded = asn2.toByteArray(); + asn.writeByte(0x30); // SEQUENCE + asn.writeData(dsaKeyEncoded); + } +} diff --git a/src/com/sshtools/j2ssh/openssh/KeyInfo.java b/src/com/sshtools/j2ssh/openssh/KeyInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..4df217d007f5a35a7ddc8d01da21ab06985f71d9 --- /dev/null +++ b/src/com/sshtools/j2ssh/openssh/KeyInfo.java @@ -0,0 +1,41 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.openssh; + +import java.security.spec.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface KeyInfo { + KeySpec getPrivateKeySpec(); + + KeySpec getPublicKeySpec(); +} diff --git a/src/com/sshtools/j2ssh/openssh/OpenSSHPrivateKeyFormat.java b/src/com/sshtools/j2ssh/openssh/OpenSSHPrivateKeyFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..7ba99e4849c2b75158096d4fb361006217c42fc3 --- /dev/null +++ b/src/com/sshtools/j2ssh/openssh/OpenSSHPrivateKeyFormat.java @@ -0,0 +1,241 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.openssh; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKeyFormat; +import com.sshtools.j2ssh.util.SimpleASNReader; +import com.sshtools.j2ssh.util.SimpleASNWriter; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; + +import java.math.BigInteger; + +import java.security.GeneralSecurityException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class OpenSSHPrivateKeyFormat implements SshPrivateKeyFormat { + /** + * Creates a new OpenSSHPrivateKeyFormat object. + */ + public OpenSSHPrivateKeyFormat() { + } + + /** + * + * + * @return + */ + public String getFormatType() { + return "OpenSSH-PrivateKey"; + } + + /** + * + * + * @return + */ + public String toString() { + return getFormatType(); + } + + /** + * + * + * @param formattedKey + * @param passphrase + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] decryptKeyblob(byte[] formattedKey, String passphrase) + throws InvalidSshKeyException { + //System.err.println("Decrypting key using passphrase " + passphrase); + try { + Reader r = new StringReader(new String(formattedKey, "US-ASCII")); + PEMReader pem = new PEMReader(r); + byte[] payload = pem.decryptPayload(passphrase); + SimpleASNReader asn = new SimpleASNReader(payload); + + if (PEM.DSA_PRIVATE_KEY.equals(pem.getType())) { + DSAKeyInfo keyInfo = DSAKeyInfo.getDSAKeyInfo(asn); + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString("ssh-dss"); + baw.writeBigInteger(keyInfo.getP()); + baw.writeBigInteger(keyInfo.getQ()); + baw.writeBigInteger(keyInfo.getG()); + baw.writeBigInteger(keyInfo.getX()); + + return baw.toByteArray(); + } else if (PEM.RSA_PRIVATE_KEY.equals(pem.getType())) { + RSAKeyInfo keyInfo = RSAKeyInfo.getRSAKeyInfo(asn); + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString("ssh-rsa"); + baw.writeBigInteger(keyInfo.getPublicExponent()); + baw.writeBigInteger(keyInfo.getModulus()); + baw.writeBigInteger(keyInfo.getPrivateExponent()); + + return baw.toByteArray(); + } else { + throw new InvalidSshKeyException("Unsupported type: " + + pem.getType()); + } + } catch (GeneralSecurityException e) { + //e.printStackTrace(); + throw new InvalidSshKeyException( + "Can't read key due to cryptography problems: " + e); + } catch (IOException e) { + //e.printStackTrace(); + throw new InvalidSshKeyException( + "Can't read key due to internal IO problems: " + e); + } + } + + /** + * + * + * @param keyblob + * @param passphrase + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] encryptKeyblob(byte[] keyblob, String passphrase) + throws InvalidSshKeyException { + try { + ByteArrayReader bar = new ByteArrayReader(keyblob); + String algorithm = bar.readString(); // dsa or rsa + byte[] payload; + PEMWriter pem = new PEMWriter(); + + if ("ssh-dss".equals(algorithm)) { + BigInteger p = bar.readBigInteger(); + BigInteger q = bar.readBigInteger(); + BigInteger g = bar.readBigInteger(); + BigInteger x = bar.readBigInteger(); + DSAKeyInfo keyInfo = new DSAKeyInfo(p, q, g, x, BigInteger.ZERO); + SimpleASNWriter asn = new SimpleASNWriter(); + DSAKeyInfo.writeDSAKeyInfo(asn, keyInfo); + payload = asn.toByteArray(); + pem.setType(PEM.DSA_PRIVATE_KEY); + } else if ("ssh-rsa".equals(algorithm)) { + BigInteger e = bar.readBigInteger(); + BigInteger n = bar.readBigInteger(); + BigInteger p = bar.readBigInteger(); + RSAKeyInfo keyInfo = new RSAKeyInfo(n, p, e, BigInteger.ZERO, + BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, + BigInteger.ZERO); + SimpleASNWriter asn = new SimpleASNWriter(); + RSAKeyInfo.writeRSAKeyInfo(asn, keyInfo); + payload = asn.toByteArray(); + pem.setType(PEM.RSA_PRIVATE_KEY); + } else { + throw new InvalidSshKeyException( + "Unsupported J2SSH algorithm: " + algorithm); + } + + pem.setPayload(payload); + pem.encryptPayload(payload, passphrase); + + StringWriter w = new StringWriter(); + pem.write(w); + + return w.toString().getBytes("US-ASCII"); + } catch (GeneralSecurityException e) { + //e.printStackTrace(); + throw new InvalidSshKeyException( + "Can't read key due to cryptography problems: " + e); + } catch (IOException e) { + //e.printStackTrace(); + throw new InvalidSshKeyException( + "Can't read key due to internal IO problems: " + e); + } + } + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isFormatted(byte[] formattedKey) { + try { + Reader r = new StringReader(new String(formattedKey, "US-ASCII")); + PEMReader pem = new PEMReader(r); + + return true; + } catch (IOException e) { + return false; + } + } + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isPassphraseProtected(byte[] formattedKey) { + try { + Reader r = new StringReader(new String(formattedKey, "US-ASCII")); + PEMReader pem = new PEMReader(r); + + return pem.getHeader().containsKey("DEK-Info"); + } catch (IOException e) { + return true; + } + } + + /** + * + * + * @param algorithm + * + * @return + */ + public boolean supportsAlgorithm(String algorithm) { + if ("ssh-dss".equals(algorithm) || "ssh-rsa".equals(algorithm)) { + return true; + } else { + return false; + } + } +} diff --git a/src/com/sshtools/j2ssh/openssh/PEM.java b/src/com/sshtools/j2ssh/openssh/PEM.java new file mode 100644 index 0000000000000000000000000000000000000000..cdc6cd273c105760cbf7197c742322f97c0baa99 --- /dev/null +++ b/src/com/sshtools/j2ssh/openssh/PEM.java @@ -0,0 +1,121 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.openssh; + +import java.io.*; + +import java.security.*; +import java.security.spec.*; + +import javax.crypto.*; +import javax.crypto.spec.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class PEM { + /** */ + public final static String DSA_PRIVATE_KEY = "DSA PRIVATE KEY"; + + /** */ + public final static String RSA_PRIVATE_KEY = "RSA PRIVATE KEY"; + + /** */ + protected final static String PEM_BOUNDARY = "-----"; + + /** */ + protected final static String PEM_BEGIN = PEM_BOUNDARY + "BEGIN "; + + /** */ + protected final static String PEM_END = PEM_BOUNDARY + "END "; + + /** */ + protected final static int MAX_LINE_LENGTH = 75; + + /** */ + protected final static char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); + private final static int MD5_HASH_BYTES = 0x10; + + /** + * + * + * @param passphrase + * @param iv + * @param keySize + * + * @return + * + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws Error + */ + protected static SecretKey getKeyFromPassphrase(String passphrase, + byte[] iv, int keySize) + throws NoSuchAlgorithmException, InvalidKeySpecException { + byte[] passphraseBytes; + + try { + passphraseBytes = passphrase.getBytes("US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new Error( + "Mandatory US-ASCII character encoding is not supported by the VM"); + } + + /* + hash is MD5 + h(0) <- hash(passphrase, iv); + h(n) <- hash(h(n-1), passphrase, iv); + key <- (h(0),...,h(n))[0,..,key.length]; + */ + MessageDigest hash = MessageDigest.getInstance("MD5"); + byte[] key = new byte[keySize]; + int hashesSize = keySize & 0xfffffff0; + + if ((keySize & 0xf) != 0) { + hashesSize += MD5_HASH_BYTES; + } + + byte[] hashes = new byte[hashesSize]; + byte[] previous; + + for (int index = 0; (index + MD5_HASH_BYTES) <= hashes.length; + hash.update(previous, 0, previous.length)) { + hash.update(passphraseBytes, 0, passphraseBytes.length); + hash.update(iv, 0, iv.length); + previous = hash.digest(); + System.arraycopy(previous, 0, hashes, index, previous.length); + index += previous.length; + } + + System.arraycopy(hashes, 0, key, 0, key.length); + + return new SecretKeySpec(key, "DESede"); + } +} diff --git a/src/com/sshtools/j2ssh/openssh/PEMReader.java b/src/com/sshtools/j2ssh/openssh/PEMReader.java new file mode 100644 index 0000000000000000000000000000000000000000..d4f1789958926385aaaf36d274875d2f5d235ec3 --- /dev/null +++ b/src/com/sshtools/j2ssh/openssh/PEMReader.java @@ -0,0 +1,215 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.openssh; + +import com.sshtools.j2ssh.util.Base64; + +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.Reader; + +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; + +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class PEMReader extends PEM { + private LineNumberReader reader; + private String type; + private Map header; + private byte[] payload; + + /** + * Creates a new PEMReader object. + * + * @param r + * + * @throws IOException + */ + public PEMReader(Reader r) throws IOException { + reader = new LineNumberReader(r); + read(); + } + + private void read() throws IOException { + String line; + + while ((line = reader.readLine()) != null) { + if (line.startsWith(PEM_BOUNDARY) && line.endsWith(PEM_BOUNDARY)) { + if (line.startsWith(PEM_BEGIN)) { + type = line.substring(PEM_BEGIN.length(), + line.length() - PEM_BOUNDARY.length()); + + break; + } else { + throw new IOException("Invalid PEM boundary at line " + + reader.getLineNumber() + ": " + line); + } + } + } + + header = new HashMap(); + + while ((line = reader.readLine()) != null) { + int colon = line.indexOf(':'); + + if (colon == -1) { + break; + } + + String key = line.substring(0, colon).trim(); + + if (line.endsWith("\\")) { + String v = line.substring(colon + 1, line.length() - 1).trim(); + + // multi-line value + StringBuffer value = new StringBuffer(v); + + while ((line = reader.readLine()) != null) { + if (line.endsWith("\\")) { + value.append(" ").append(line.substring(0, + line.length() - 1).trim()); + } else { + value.append(" ").append(line.trim()); + + break; + } + } + } else { + String value = line.substring(colon + 1).trim(); + header.put(key, value); + } + } + + // first line that is not part of the header + // could be an empty line, but if there is no header and the body begins straight after the ----- + // then this line contains data + if (line == null) { + throw new IOException( + "The key format is invalid! OpenSSH formatted keys must begin with -----BEGIN RSA or -----BEGIN DSA"); + } + + StringBuffer body = new StringBuffer(line); + + while ((line = reader.readLine()) != null) { + if (line.startsWith(PEM_BOUNDARY) && line.endsWith(PEM_BOUNDARY)) { + if (line.startsWith(PEM_END + type)) { + break; + } else { + throw new IOException("Invalid PEM end boundary at line " + + reader.getLineNumber() + ": " + line); + } + } + + body.append(line); + } + + payload = Base64.decode(body.toString()); + } + + /** + * + * + * @return + */ + public Map getHeader() { + return header; + } + + /** + * + * + * @return + */ + public byte[] getPayload() { + return payload; + } + + /** + * + * + * @return + */ + public String getType() { + return type; + } + + /** + * + * + * @param passphrase + * + * @return + * + * @throws GeneralSecurityException + * @throws NoSuchAlgorithmException + */ + public byte[] decryptPayload(String passphrase) + throws GeneralSecurityException { + String dekInfo = (String) header.get("DEK-Info"); + + if (dekInfo != null) { + int comma = dekInfo.indexOf(','); + String keyAlgorithm = dekInfo.substring(0, comma); + + if (!"DES-EDE3-CBC".equals(keyAlgorithm)) { + throw new NoSuchAlgorithmException( + "Unsupported passphrase algorithm: " + keyAlgorithm); + } + + String ivString = dekInfo.substring(comma + 1); + byte[] iv = new byte[ivString.length() / 2]; + + for (int i = 0; i < ivString.length(); i += 2) { + iv[i / 2] = (byte) Integer.parseInt(ivString.substring(i, i + + 2), 16); + } + + Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); + SecretKey key = getKeyFromPassphrase(passphrase, iv, 24); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); + + byte[] plain = new byte[payload.length]; + cipher.update(payload, 0, payload.length, plain, 0); + + return plain; + } else { + return payload; + } + } +} diff --git a/src/com/sshtools/j2ssh/openssh/PEMWriter.java b/src/com/sshtools/j2ssh/openssh/PEMWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..c25bfef100f651a360f4ef4598ac63e9d3630732 --- /dev/null +++ b/src/com/sshtools/j2ssh/openssh/PEMWriter.java @@ -0,0 +1,190 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.openssh; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.util.Base64; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +import java.security.GeneralSecurityException; +import java.security.SecureRandom; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class PEMWriter extends PEM { + private String type; + private Map header = new HashMap(); + private byte[] payload; + + /** + * Creates a new PEMWriter object. + */ + public PEMWriter() { + } + + /** + * + * + * @param w + * + * @throws IOException + */ + public void write(Writer w) throws IOException { + PrintWriter writer = new PrintWriter(w, true); + writer.println(PEM_BEGIN + type + PEM_BOUNDARY); + + if (!header.isEmpty()) { + for (Iterator i = header.keySet().iterator(); i.hasNext();) { + String key = (String) i.next(); + String value = (String) header.get(key); + writer.print(key + ": "); + + if ((key.length() + value.length() + 2) > MAX_LINE_LENGTH) { + int offset = Math.max(MAX_LINE_LENGTH - key.length() - 2, 0); + writer.println(value.substring(0, offset) + "\\"); + + for (; offset < value.length(); + offset += MAX_LINE_LENGTH) { + if ((offset + MAX_LINE_LENGTH) >= value.length()) { + writer.println(value.substring(offset)); + } else { + writer.println(value.substring(offset, + offset + MAX_LINE_LENGTH) + "\\"); + } + } + } else { + writer.println(value); + } + } + + writer.println(); + } + + writer.println(Base64.encodeBytes(payload, false)); + writer.println(PEM_END + type + PEM_BOUNDARY); + } + + /** + * + * + * @param payload + * @param passphrase + * + * @throws GeneralSecurityException + */ + public void encryptPayload(byte[] payload, String passphrase) + throws GeneralSecurityException { + if ((passphrase == null) || (passphrase.length() == 0)) { + // Simple case: no passphrase means no encryption of the private key + setPayload(payload); + + return; + } + + SecureRandom rnd = ConfigurationLoader.getRND(); + byte[] iv = new byte[8]; + rnd.nextBytes(iv); + + StringBuffer ivString = new StringBuffer(16); + + for (int i = 0; i < iv.length; i++) { + ivString.append(HEX_CHARS[(iv[i] & 0xff) >> 4]); + ivString.append(HEX_CHARS[iv[i] & 0x0f]); + } + + header.put("DEK-Info", "DES-EDE3-CBC," + ivString); + header.put("Proc-Type", "4,ENCRYPTED"); + + Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); + SecretKey key = getKeyFromPassphrase(passphrase, iv, 24); + cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); + + byte[] encrypted = new byte[payload.length]; + cipher.update(payload, 0, payload.length, encrypted, 0); + setPayload(encrypted); + } + + /** + * + * + * @return + */ + public Map getHeader() { + return header; + } + + /** + * + * + * @return + */ + public byte[] getPayload() { + return payload; + } + + /** + * + * + * @return + */ + public String getType() { + return type; + } + + /** + * + * + * @param bs + */ + public void setPayload(byte[] bs) { + payload = bs; + } + + /** + * + * + * @param string + */ + public void setType(String string) { + type = string; + } +} diff --git a/src/com/sshtools/j2ssh/openssh/RSAKeyInfo.java b/src/com/sshtools/j2ssh/openssh/RSAKeyInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..5e2fff41c66896d65a02eed07d6f7a976526353e --- /dev/null +++ b/src/com/sshtools/j2ssh/openssh/RSAKeyInfo.java @@ -0,0 +1,267 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.openssh; + +import com.sshtools.j2ssh.util.SimpleASNReader; +import com.sshtools.j2ssh.util.SimpleASNWriter; + +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.spec.KeySpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class RSAKeyInfo implements KeyInfo { + private BigInteger modulus; + private BigInteger publicExponent; + private BigInteger privateExponent; + private BigInteger primeP; + private BigInteger primeQ; + private BigInteger primeExponentP; + private BigInteger primeExponentQ; + private BigInteger crtCoefficient; + + /** + * Creates a new RSAKeyInfo object. + * + * @param modulus + * @param publicExponent + * @param privateExponent + * @param primeP + * @param primeQ + * @param primeExponentP + * @param primeExponentQ + * @param crtCoefficient + */ + public RSAKeyInfo(BigInteger modulus, BigInteger publicExponent, + BigInteger privateExponent, BigInteger primeP, BigInteger primeQ, + BigInteger primeExponentP, BigInteger primeExponentQ, + BigInteger crtCoefficient) { + this.modulus = modulus; + this.publicExponent = publicExponent; + this.privateExponent = privateExponent; + this.primeP = primeP; + this.primeQ = primeQ; + this.primeExponentP = primeExponentP; + this.primeExponentQ = primeExponentQ; + this.crtCoefficient = crtCoefficient; + } + + /** + * + * + * @return + */ + public KeySpec getPrivateKeySpec() { + return new RSAPrivateKeySpec(modulus, privateExponent); + + // return new RSAPrivateCrtKeySpec( + // modulus, + // publicExponent, + // privateExponent, + // primeP, + // primeQ, + // primeExponentP, + // primeExponentQ, + // crtCoefficient); + } + + /** + * + * + * @return + */ + public KeySpec getPublicKeySpec() { + return new RSAPublicKeySpec(modulus, publicExponent); + } + + /** + * + * + * @return + */ + public BigInteger getCrtCoefficient() { + return crtCoefficient; + } + + /** + * + * + * @return + */ + public BigInteger getModulus() { + return modulus; + } + + /** + * + * + * @return + */ + public BigInteger getPrimeExponentP() { + return primeExponentP; + } + + /** + * + * + * @return + */ + public BigInteger getPrimeExponentQ() { + return primeExponentQ; + } + + /** + * + * + * @return + */ + public BigInteger getPrimeP() { + return primeP; + } + + /** + * + * + * @return + */ + public BigInteger getPrimeQ() { + return primeQ; + } + + /** + * + * + * @return + */ + public BigInteger getPrivateExponent() { + return privateExponent; + } + + /** + * + * + * @return + */ + public BigInteger getPublicExponent() { + return publicExponent; + } + + /** + * + * + * @param asn + * + * @return + * + * @throws IOException + */ + public static RSAKeyInfo getRSAKeyInfo(SimpleASNReader asn) + throws IOException { + asn.assertByte(0x30); // SEQUENCE + + int length = asn.getLength(); + asn.assertByte(0x02); // INTEGER (version) + + byte[] version = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] modulus = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] publicExponent = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] privateExponent = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] primeP = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] primeQ = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] primeExponentP = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] primeExponentQ = asn.getData(); + asn.assertByte(0x02); // INTEGER () + + byte[] crtCoefficient = asn.getData(); + + return new RSAKeyInfo(new BigInteger(modulus), + new BigInteger(publicExponent), new BigInteger(privateExponent), + new BigInteger(primeP), new BigInteger(primeQ), + new BigInteger(primeExponentP), new BigInteger(primeExponentQ), + new BigInteger(crtCoefficient)); + } + + /** + * + * + * @param asn + * @param keyInfo + */ + public static void writeRSAKeyInfo(SimpleASNWriter asn, RSAKeyInfo keyInfo) { + // Write to a substream temporarily. + // This code needs to know the length of the substream before it can write the data from + // the substream to the main stream. + SimpleASNWriter asn2 = new SimpleASNWriter(); + asn2.writeByte(0x02); // INTEGER (version) + + byte[] version = new byte[1]; + asn2.writeData(version); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getModulus().toByteArray()); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getPublicExponent().toByteArray()); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getPrivateExponent().toByteArray()); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getPrimeP().toByteArray()); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getPrimeQ().toByteArray()); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getPrimeExponentP().toByteArray()); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getPrimeExponentQ().toByteArray()); + asn2.writeByte(0x02); // INTEGER () + asn2.writeData(keyInfo.getCrtCoefficient().toByteArray()); + + byte[] rsaKeyEncoded = asn2.toByteArray(); + asn.writeByte(0x30); // SEQUENCE + asn.writeData(rsaKeyEncoded); + } +} diff --git a/src/com/sshtools/j2ssh/session/PseudoTerminal.java b/src/com/sshtools/j2ssh/session/PseudoTerminal.java new file mode 100644 index 0000000000000000000000000000000000000000..0ba18308b89bc941754c5ee4f6f8930b53207b62 --- /dev/null +++ b/src/com/sshtools/j2ssh/session/PseudoTerminal.java @@ -0,0 +1,233 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.session; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public interface PseudoTerminal { + /** */ + public final int TTY_OP_END = 0; //Indicates end of options. + + /** */ + public final int VINTR = 1; //Interrupt character; 255 if none. Similarly for the + + /** */ + public final int VQUIT = 2; //The quit character (sends SIGQUIT signal on POSIX + public final int VERASE = 3; //Erase the character to left of the cursor. + + /** */ + public final int VKILL = 4; //Kill the current input line. + + /** */ + public final int VEOF = 5; //End-of-file character (sends EOF from the terminal). + + /** */ + public final int VEOL = 6; //End-of-line character in addition to carriage return + public final int VEOL2 = 7; //Additional end-of-line character. + + /** */ + public final int VSTART = 8; //Continues paused output (normally control-Q). + + /** */ + public final int VSTOP = 9; //Pauses output (normally control-S). + + /** */ + public final int VSUSP = 10; //Suspends the current program. + + /** */ + public final int VDSUSP = 11; //Another suspend character. + + /** */ + public final int VREPRINT = 12; //Reprints the current input line. + + /** */ + public final int VWERASE = 13; //Erases a word left of cursor. + + /** */ + public final int VLNEXT = 14; //Enter the next character typed literally, even if it + public final int VFLUSH = 15; //Character to flush output. + + /** */ + public final int VSWTCH = 16; //Switch to a different shell layer. + + /** */ + public final int VSTATUS = 17; //Prints system status line (load, command, pid etc). + + /** */ + public final int VDISCARD = 18; //Toggles the flushing of terminal output. + + /** */ + public final int IGNPAR = 30; //The ignore parity flag. The parameter SHOULD be 0 if + public final int PARMRK = 31; //Mark parity and framing errors. + + /** */ + public final int INPCK = 32; //Enable checking of parity errors. + + /** */ + public final int ISTRIP = 33; //Strip 8th bit off characters. + + /** */ + public final int INLCR = 34; //Map NL into CR on input. + + /** */ + public final int IGNCR = 35; //Ignore CR on input. + + /** */ + public final int ICRNL = 36; //Map CR to NL on input. + + /** */ + public final int IUCLC = 37; //Translate uppercase characters to lowercase. + + /** */ + public final int IXON = 38; //Enable output flow control. + + /** */ + public final int IXANY = 39; //Any char will restart after stop. + + /** */ + public final int IXOFF = 40; //Enable input flow control. + + /** */ + public final int IMAXBEL = 41; //Ring bell on input queue full. + + /** */ + public final int ISIG = 50; //Enable signals INTR, QUIT, [D]SUSP. + + /** */ + public final int ICANON = 51; //Canonicalize input lines. + + /** */ + public final int XCASE = 52; //Enable input and output of uppercase characters by + public final int ECHO = 53; //Enable echoing. + + /** */ + public final int ECHOE = 54; //Visually erase chars. + + /** */ + public final int ECHOK = 55; //Kill character discards current line. + + /** */ + public final int ECHONL = 56; //Echo NL even if ECHO is off. + + /** */ + public final int NOFLSH = 57; //Don't flush after interrupt. + + /** */ + public final int TOSTOP = 58; //Stop background jobs from output. + + /** */ + public final int IEXTEN = 59; //Enable extensions. + + /** */ + public final int ECHOCTL = 60; //Echo control characters as ^(Char). + + /** */ + public final int ECHOKE = 61; //Visual erase for line kill. + + /** */ + public final int PENDIN = 62; //Retype pending input. + + /** */ + public final int OPOST = 70; //Enable output processing. + + /** */ + public final int OLCUC = 71; //Convert lowercase to uppercase. + + /** */ + public final int ONLCR = 72; //Map NL to CR-NL. + + /** */ + public final int OCRNL = 73; //Translate carriage return to newline (output). + + /** */ + public final int ONOCR = 74; //Translate newline to carriage return-newline + public final int ONLRET = 75; //Newline performs a carriage return (output). + + /** */ + public final int CS7 = 90; //7 bit mode. + + /** */ + public final int CS8 = 91; //8 bit mode. + + /** */ + public final int PARENB = 92; //Parity enable. + + /** */ + public final int PARODD = 93; //Odd parity, else even. + + /** */ + public final int TTY_OP_ISPEED = 128; //Specifies the input baud rate in bits per second. + + /** */ + public final int TTY_OP_OSPEED = 129; //Specifies the output baud rate in bits per second. + + /** + * + * + * @return + */ + public int getColumns(); + + /** + * + * + * @return + */ + public String getEncodedTerminalModes(); + + /** + * + * + * @return + */ + public int getHeight(); + + /** + * + * + * @return + */ + public int getRows(); + + /** + * + * + * @return + */ + public String getTerm(); + + /** + * + * + * @return + */ + public int getWidth(); +} diff --git a/src/com/sshtools/j2ssh/session/SessionChannelClient.java b/src/com/sshtools/j2ssh/session/SessionChannelClient.java new file mode 100644 index 0000000000000000000000000000000000000000..22f79e9c3430d833a32b3e928a42a4a8abfffffc --- /dev/null +++ b/src/com/sshtools/j2ssh/session/SessionChannelClient.java @@ -0,0 +1,555 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.session; + +import com.sshtools.j2ssh.SshException; +import com.sshtools.j2ssh.agent.AgentSocketChannel; +import com.sshtools.j2ssh.agent.SshAgentClient; +import com.sshtools.j2ssh.connection.Channel; +import com.sshtools.j2ssh.connection.ChannelFactory; +import com.sshtools.j2ssh.connection.ChannelInputStream; +import com.sshtools.j2ssh.connection.IOChannel; +import com.sshtools.j2ssh.connection.InvalidChannelException; +import com.sshtools.j2ssh.connection.SshMsgChannelExtendedData; +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemClient; +import com.sshtools.j2ssh.transport.SshMessageStore; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.Socket; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.52 $ + */ +public class SessionChannelClient extends IOChannel { + private static Log log = LogFactory.getLog(SessionChannelClient.class); + private Integer exitCode = null; + private String sessionType = "Uninitialized"; + private SubsystemClient subsystem; + private boolean localFlowControl = false; + private SignalListener signalListener; + private SshMessageStore errorMessages = new SshMessageStore(); + private ChannelInputStream stderr = new ChannelInputStream( + /*ChannelInputStream.createExtended(*/ + errorMessages, + new Integer(SshMsgChannelExtendedData.SSH_EXTENDED_DATA_STDERR)); + + // new Integer(SshMsgChannelExtendedData.SSH_EXTENDED_DATA_STDERR)); + + /** + * Creates a new SessionChannelClient object. + */ + public SessionChannelClient() { + super(); + setName("session"); + } + + /** + * + * + * @return + */ + public byte[] getChannelOpenData() { + return null; + } + + /** + * + * + * @return + */ + public byte[] getChannelConfirmationData() { + return null; + } + + /** + * + * + * @return + */ + public String getChannelType() { + return "session"; + } + + /** + * + * + * @return + */ + protected int getMinimumWindowSpace() { + return 1024; + } + + /** + * + * + * @return + */ + protected int getMaximumWindowSpace() { + return 32648; + } + + /** + * + * + * @return + */ + protected int getMaximumPacketSize() { + return 32648; + } + + /** + * + * + * @param signalListener + */ + public void setSignalListener(SignalListener signalListener) { + this.signalListener = signalListener; + } + + /** + * + * + * @param name + * @param value + * + * @return + * + * @throws IOException + */ + public boolean setEnvironmentVariable(String name, String value) + throws IOException { + log.debug("Requesting environment variable to be set [" + name + "=" + + value + "]"); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(name); + baw.writeString(value); + + return connection.sendChannelRequest(this, "env", true, + baw.toByteArray()); + } + + /** + * + * + * @return + * + * @throws IOException + * @throws SshException + * @throws InvalidChannelException + */ + public boolean requestAgentForwarding() throws IOException { + log.info("Requesting agent forwarding for the session"); + + if (System.getProperty("sshtools.agent") == null) { + throw new SshException( + "Agent not found! 'sshtools.agent' system property should identify the agent location"); + } + + boolean success = connection.sendChannelRequest(this, "auth-agent-req", + true, null); + + if (success) { + // Allow an Agent Channel to be opened + connection.addChannelFactory(AgentSocketChannel.AGENT_FORWARDING_CHANNEL, + new ChannelFactory() { + public Channel createChannel(String channelType, + byte[] requestData) throws InvalidChannelException { + try { + AgentSocketChannel channel = new AgentSocketChannel(false); + Socket socket = SshAgentClient.connectAgentSocket(System.getProperty( + "sshtools.agent") /*, 5*/); + channel.bindSocket(socket); + + return channel; + } catch (Exception ex) { + throw new InvalidChannelException(ex.getMessage()); + } + } + }); + } + + return success; + } + + /** + * + * + * @param display + * @param cookie + * + * @return + * + * @throws IOException + */ + public boolean requestX11Forwarding(int display, String cookie) + throws IOException { + log.debug("Requesting X11 forwarding for display " + display + + " using cookie " + cookie); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeBoolean(false); + baw.writeString("MIT-MAGIC-COOKIE-1"); + baw.writeString(cookie); + baw.writeUINT32(new UnsignedInteger32(String.valueOf(display))); + + return connection.sendChannelRequest(this, "x11-req", true, + baw.toByteArray()); + } + + /** + * + * + * @return + */ + public Integer getExitCode() { + return exitCode; + } + + /** + * + * + * @param term + * + * @throws IOException + */ + public void changeTerminalDimensions(PseudoTerminal term) + throws IOException { + log.debug("Changing terminal dimensions"); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeInt(term.getColumns()); + baw.writeInt(term.getRows()); + baw.writeInt(term.getWidth()); + baw.writeInt(term.getHeight()); + connection.sendChannelRequest(this, "window-change", false, + baw.toByteArray()); + } + + /** + * + * + * @param command + * + * @return + * + * @throws IOException + */ + public boolean executeCommand(String command) throws IOException { + log.info("Requesting command execution"); + log.info("Command is " + command); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(command); + + if( connection.sendChannelRequest(this, "exec", true, baw.toByteArray()) ) + { + if (sessionType.equals("Uninitialized")) { + sessionType = command; + } + + return true; + } else { + return false; + } + } + + /** + * + * + * @param term + * @param cols + * @param rows + * @param width + * @param height + * @param terminalModes + * + * @return + * + * @throws IOException + */ + public boolean requestPseudoTerminal(String term, int cols, int rows, + int width, int height, String terminalModes) throws IOException { + log.info("Requesting pseudo terminal"); + + if (log.isDebugEnabled()) { + log.debug("Terminal Type is " + term); + log.debug("Columns=" + String.valueOf(cols)); + log.debug("Rows=" + String.valueOf(rows)); + log.debug("Width=" + String.valueOf(width)); + log.debug("Height=" + String.valueOf(height)); + } + + // This requests a pseudo terminal + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(term); + baw.writeInt(cols); + baw.writeInt(rows); + baw.writeInt(width); + baw.writeInt(height); + baw.writeString(terminalModes); + + return connection.sendChannelRequest(this, "pty-req", true, + baw.toByteArray()); + } + + /** + * + * + * @param term + * + * @return + * + * @throws IOException + */ + public boolean requestPseudoTerminal(PseudoTerminal term) + throws IOException { + return requestPseudoTerminal(term.getTerm(), term.getColumns(), + term.getRows(), term.getWidth(), term.getHeight(), + term.getEncodedTerminalModes()); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public boolean startShell() throws IOException { + log.debug("Requesting users shell"); + + // Send the request for a shell, we want a reply + if (connection.sendChannelRequest(this, "shell", true, null)) { + if (sessionType.equals("Uninitialized")) { + sessionType = "shell"; + } + + return true; + } else { + return false; + } + } + + /** + * + * + * @param subsystem + * + * @return + * + * @throws IOException + */ + public boolean startSubsystem(String subsystem) throws IOException { + log.info("Starting " + subsystem + " subsystem"); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(subsystem); + + if (connection.sendChannelRequest(this, "subsystem", true, + baw.toByteArray())) { + if (sessionType.equals("Uninitialized")) { + sessionType = subsystem; + } + + return true; + } else { + return false; + } + } + + /** + * + * + * @param subsystem + * + * @return + * + * @throws IOException + */ + public boolean startSubsystem(SubsystemClient subsystem) + throws IOException { + boolean result = startSubsystem(subsystem.getName()); + + if (result) { + this.subsystem = subsystem; + subsystem.setSessionChannel(this); + subsystem.start(); + } + + return result; + } + + /** + * + * + * @return + */ + public boolean isLocalFlowControlEnabled() { + return localFlowControl; + } + + /** + * + * + * @return + */ + public String getSessionType() { + return sessionType; + } + + /** + * + * + * @param sessionType + */ + public void setSessionType(String sessionType) { + this.sessionType = sessionType; + } + + /** + * + * + * @return + */ + public SubsystemClient getSubsystem() { + return subsystem; + } + + /** + * + * + * @throws IOException + */ + protected void onChannelClose() throws IOException { + super.onChannelClose(); + + try { + stderr.close(); + } catch (IOException ex) { + } + + Integer exitCode = getExitCode(); + + if (exitCode != null) { + log.debug("Exit code " + exitCode.toString()); + } + } + + /** + * + * + * @throws IOException + */ + protected void onChannelOpen() throws IOException { + } + + /** + * + * + * @return + * + * @throws IOException + */ + public InputStream getStderrInputStream() throws IOException { + /*if (stderr == null) { + throw new IOException("The session must be started first!"); + }*/ + return stderr; + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected void onChannelExtData(SshMsgChannelExtendedData msg) + throws IOException { + errorMessages.addMessage(msg); + } + + /** + * + * + * @param requestType + * @param wantReply + * @param requestData + * + * @throws IOException + */ + protected void onChannelRequest(String requestType, boolean wantReply, + byte[] requestData) throws IOException { + log.info("Channel Request received: " + requestType); + + if (requestType.equals("exit-status")) { + exitCode = new Integer((int) ByteArrayReader.readInt(requestData, 0)); + //log.debug("Exit code of " + exitCode.toString() + " received"); + log.info("Exit code of " + exitCode.toString() + " received"); + } else if (requestType.equals("exit-signal")) { + ByteArrayReader bar = new ByteArrayReader(requestData); + String signal = bar.readString(); + boolean coredump = bar.read() != 0; + String message = bar.readString(); + String language = bar.readString(); + log.debug("Exit signal " + signal + " received"); + log.debug("Signal message: " + message); + log.debug("Core dumped: " + String.valueOf(coredump)); + + if (signalListener != null) { + signalListener.onExitSignal(signal, coredump, message); + } + } else if (requestType.equals("xon-xoff")) { + if (requestData.length >= 1) { + localFlowControl = (requestData[0] != 0); + } + } else if (requestType.equals("signal")) { + String signal = ByteArrayReader.readString(requestData, 0); + log.debug("Signal " + signal + " received"); + + if (signalListener != null) { + signalListener.onSignal(signal); + } + } else { + if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + } +} diff --git a/src/com/sshtools/j2ssh/session/SessionOutputEcho.java b/src/com/sshtools/j2ssh/session/SessionOutputEcho.java new file mode 100644 index 0000000000000000000000000000000000000000..dda3b3cd05198c297c44a044542f017a49dedc02 --- /dev/null +++ b/src/com/sshtools/j2ssh/session/SessionOutputEcho.java @@ -0,0 +1,56 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.session; + + +/** + * <p> + * Title: + * </p> + * + * <p> + * Description: + * </p> + * + * <p> + * Copyright: Copyright (c) 2003 + * </p> + * + * <p> + * Company: + * </p> + * + * @author Lee David Painter + * @version $Id: SessionOutputEcho.java,v 1.9 2003/09/11 15:35:13 martianx Exp $ + */ +public interface SessionOutputEcho { + /** + * + * + * @param msg + */ + public void echo(String msg); +} diff --git a/src/com/sshtools/j2ssh/session/SessionOutputReader.java b/src/com/sshtools/j2ssh/session/SessionOutputReader.java new file mode 100644 index 0000000000000000000000000000000000000000..065f0672dfe069510a2dec117fdca800bc8d5681 --- /dev/null +++ b/src/com/sshtools/j2ssh/session/SessionOutputReader.java @@ -0,0 +1,254 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.session; + +import com.sshtools.j2ssh.connection.*; + + +/** + * <p> + * This class provides a utility to read and parse the output a session, + * providing methods to wait for specific strings such as the prompt or + * command input requests. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.13 $ + * + * @since 0.2.1 + */ +public class SessionOutputReader { + SessionChannelClient session; + int pos = 0; + int mark = 0; + String output = ""; + + /** + * <p> + * Contructs the session reader. + * </p> + * + * @param session the to read + */ + public SessionOutputReader(SessionChannelClient session) { + this.session = session; + session.addEventListener(new SessionOutputListener()); + } + + /** + * Returns the output of the entire session. + * + * @return a string containing the entire output of the session so far. + */ + public String getOutput() { + return output; + } + + /** + * <p> + * Returns the current position of the session input pointer. This pointer + * is set to the position of the matched string everytime a match is found + * during a call by <code>waitForString</code> + * </p> + * + * @return the current input reader pointer + */ + public int getPosition() { + return pos; + } + + /** + * Mark the postion specified for filtering session output. + * + * @param mark output position to mark + */ + public void markPosition(int mark) { + this.mark = mark; + } + + /** + * Marks the current position. + */ + public void markCurrentPosition() { + this.mark = pos; + } + + /** + * <p> + * Returns a string containing the session output from the current marked + * position to the end of the output. + * </p> + * + * @return a string containing the session output from the marked position + * to current position + */ + public String getMarkedOutput() { + return output.substring(mark, pos); + } + + /** + * <p> + * Wait for a given String in the output buffer. + * </p> + * + * @param str the string to wait for + * @param echo a callback interface to receive the session output whilst + * the no match for the string is found + * + * @return true if the string was found, otherwise false + * + * @throws InterruptedException if the thread is interrupted + * + * @see waitForString(String, int, SessionOutputEcho) + */ + public synchronized boolean waitForString(String str, SessionOutputEcho echo) + throws InterruptedException { + return waitForString(str, 0, echo); + } + + /** + * <p> + * Wait for a given String in the output buffer. This method will block + * until the string is found. + * </p> + * + * @param str the string to wait for + * + * @return true if the string was found, otherwise false + * + * @throws InterruptedException if the thread is interrupted + * + * @see waitForString(String, int, SessionOutputEcho) + */ + public synchronized boolean waitForString(String str) + throws InterruptedException { + return waitForString(str, 0, null); + } + + /** + * <p> + * Wait for a given String in the output buffer. + * </p> + * + * @param str the string to wait for + * @param timeout the number of milliseconds to wait + * + * @return true if the string was found, otherwise false + * + * @throws InterruptedException if the thread is interrupted + * + * @see waitForString(String, int, SessionOutputEcho) + */ + public synchronized boolean waitForString(String str, int timeout) + throws InterruptedException { + return waitForString(str, timeout, null); + } + + /** + * <p> + * Wait for a given String in the output buffer. When this method is called + * the method will block unitil either the String arrives in the input + * buffer or the timeout specified has elasped. + * </p> + * + * @param str the string to wait for + * @param timeout the number of milliseconds to wait, 0=infinite + * @param echo a callback interface to receive the session output whilst + * the no match for the string is found + * + * @return true if the string was found, otherwise false + * + * @throws InterruptedException if the thread is interrupted + */ + public synchronized boolean waitForString(String str, int timeout, + SessionOutputEcho echo) throws InterruptedException { + long time = System.currentTimeMillis(); + + while ((output.indexOf(str, pos) == -1) && + (((System.currentTimeMillis() - time) < timeout) || + (timeout == 0))) { + int tmp = output.length(); + wait((timeout > 0) ? (timeout - + (System.currentTimeMillis() - time)) : 0); + + if ((output.length() > tmp) && (echo != null)) { + echo.echo(output.substring(tmp, output.length())); + } + } + + if (output.indexOf(str, pos) > -1) { + pos = output.indexOf(str, pos) + str.length(); + + return true; + } else { + return false; + } + } + + /** + * + * + * @param echo + * + * @throws InterruptedException + */ + public synchronized void echoLineByLineToClose(SessionOutputEcho echo) + throws InterruptedException { + while (session.isOpen()) { + waitForString("\n", 1000, echo); + } + } + + private synchronized void breakWaiting() { + notifyAll(); + } + + /** + * The ChannelEventListener to receive event notifications + */ + class SessionOutputListener implements ChannelEventListener { + public void onChannelOpen(Channel channel) { + } + + public void onChannelClose(Channel channel) { + breakWaiting(); + } + + public void onChannelEOF(Channel channel) { + // Timeout + breakWaiting(); + } + + public void onDataSent(Channel channel, byte[] data) { + // Do nothing + } + + public void onDataReceived(Channel channel, byte[] data) { + output += new String(data); + breakWaiting(); + } + } +} diff --git a/src/com/sshtools/j2ssh/session/SignalListener.java b/src/com/sshtools/j2ssh/session/SignalListener.java new file mode 100644 index 0000000000000000000000000000000000000000..673acdcba151f9a9b9a0d3c2fb1bb7e8991509d6 --- /dev/null +++ b/src/com/sshtools/j2ssh/session/SignalListener.java @@ -0,0 +1,51 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.session; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public interface SignalListener { + /** + * + * + * @param signal + */ + public void onSignal(String signal); + + /** + * + * + * @param signal + * @param coredump + * @param message + */ + public void onExitSignal(String signal, boolean coredump, String message); +} diff --git a/src/com/sshtools/j2ssh/sftp/FileAttributes.java b/src/com/sshtools/j2ssh/sftp/FileAttributes.java new file mode 100644 index 0000000000000000000000000000000000000000..33153135b3600073e9f62036af65e68e202bf2ff --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/FileAttributes.java @@ -0,0 +1,740 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.*; + +import java.io.*; + +import java.text.*; + +import java.util.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class FileAttributes { + // Version 4 types + + public static final int SSH_FILEXFER_TYPE_REGULAR = 1; + public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2; + public static final int SSH_FILEXFER_TYPE_SYMLINK = 3; + public static final int SSH_FILEXFER_TYPE_SPECIAL = 4; + public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5; + public static final int SSH_FILEXFER_TYPE_SOCKET = 6; + public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7; + public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8; + public static final int SSH_FILEXFER_TYPE_FIFO = 9; + + private static final int SSH_FILEXFER_ATTR_SIZE = 0x0000001; + private static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002; + private static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x0000004; + + // Also as ACMODTIME for version 3 + private static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x0000008; + private static final int SSH_FILEXFER_ATTR_CREATETIME = 0x0000010; + private static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x0000020; + private static final int SSH_FILEXFER_ATTR_ACL = 0x0000040; + private static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x0000080; + private static final int SSH_FILEXFER_ATTR_EXTENDED = 0x8000000; + + // Posix stats + + public static final int S_IFMT = 0xF000; + public static final int S_IFSOCK = 0xC000; + public static final int S_IFLNK = 0xA000; + public static final int S_IFREG = 0x8000; + public static final int S_IFBLK = 0x6000; + public static final int S_IFDIR = 0x4000; + public static final int S_IFCHR = 0x2000; + public static final int S_IFIFO = 0x1000; + public final static int S_ISUID = 0x800; + public final static int S_ISGID = 0x400; + public final static int S_IRUSR = 0x100; + public final static int S_IWUSR = 0x80; + public final static int S_IXUSR = 0x40; + public final static int S_IRGRP = 0x20; + public final static int S_IWGRP = 0x10; + public final static int S_IXGRP = 0x08; + public final static int S_IROTH = 0x04; + public final static int S_IWOTH = 0x02; + public final static int S_IXOTH = 0x01; + int version = 3; + long flags = 0x0000000; // Version 3 & 4 + int type; // Version 4 only + UnsignedInteger64 size = null; // Version 3 & 4 + UnsignedInteger32 uid = null; // Version 3 only + UnsignedInteger32 gid = null; // Version 3 only + String owner = null; // Version 4 only + String group = null; // Version 4 only + UnsignedInteger32 permissions = null; // Version 3 & 4 + UnsignedInteger32 atime = null; // Version 3 & 4 + UnsignedInteger32 createtime = null; // Version 4 only + UnsignedInteger32 mtime = null; // Version 3 & 4 + List acl = new Vector(); // Version 4 only + Map extended = new HashMap(); // Version 3 & 4 + char[] types = { 'p', 'c', 'd', 'b', '-', 'l', 's', }; + + /** + * Creates a new FileAttributes object. + */ + public FileAttributes() { + } + + /* + public FileAttributes(int type, int version) { + if(type >= 1 && type <= 5) { + this.type = type; + this.version = version; + } + else + throw new IllegalArgumentException("The type must be a valid FileAttribute type"); + } + */ + public FileAttributes(ByteArrayReader bar) throws IOException { + flags = bar.readInt(); + + /* + type = bar.readInt(); + */ + if (isFlagSet(SSH_FILEXFER_ATTR_SIZE)) { + size = bar.readUINT64(); + } + + if (isFlagSet(SSH_FILEXFER_ATTR_UIDGID)) { + uid = bar.readUINT32(); + gid = bar.readUINT32(); + } + + /*if(isFlagSet(SSH_FILEXFER_ATTR_OWNERGROUP)) { + owner = bar.readString(); + group = bar.readString(); + }*/ + if (isFlagSet(SSH_FILEXFER_ATTR_PERMISSIONS)) { + permissions = bar.readUINT32(); + } + + if (isFlagSet(SSH_FILEXFER_ATTR_ACCESSTIME)) { + atime = bar.readUINT32(); + mtime = bar.readUINT32(); + } + + /* + if(isFlagSet(SSH_FILEXFER_ATTR_CREATETIME)) + createtime = bar.readUINT32(); + if(isFlagSet(SSH_FILEXFER_ATTR_MODIFYTIME)) + mtime = bar.readUINT32(); + */ + /* + if(isFlagSet(SSH_FILEXFER_ATTR_ACL)) { + int count = (int)bar.readInt(); + for(int i=0;i<count;i++) { + acl.add(new ACL(bar)); + } + }*/ + if (isFlagSet(SSH_FILEXFER_ATTR_EXTENDED)) { + int count = (int) bar.readInt(); + String type; + String data; + + for (int i = 0; i < count; i++) { + type = bar.readString(); + data = bar.readString(); + extended.put(type, data); + } + } + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getUID() { + if (uid != null) { + return uid; + } else { + return new UnsignedInteger32(0); + } + } + + /** + * + * + * @param uid + */ + public void setUID(UnsignedInteger32 uid) { + flags |= SSH_FILEXFER_ATTR_UIDGID; + this.uid = uid; + } + + /** + * + * + * @param gid + */ + public void setGID(UnsignedInteger32 gid) { + flags |= SSH_FILEXFER_ATTR_UIDGID; + this.gid = gid; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getGID() { + if (gid != null) { + return gid; + } else { + return new UnsignedInteger32(0); + } + } + + /** + * + * + * @param size + */ + public void setSize(UnsignedInteger64 size) { + this.size = size; + + // Set the flag + if (size != null) { + flags |= SSH_FILEXFER_ATTR_SIZE; + } else { + flags ^= SSH_FILEXFER_ATTR_SIZE; + } + } + + /** + * + * + * @return + */ + public UnsignedInteger64 getSize() { + if (size != null) { + return size; + } else { + return new UnsignedInteger64("0"); + } + } + + /* + public void setOwner(String owner) { + this.owner = owner; + // Set the flag + if(group!=null || owner!=null) + flags |= SSH_FILEXFER_ATTR_OWNERGROUP; + else + flags ^= SSH_FILEXFER_ATTR_OWNERGROUP; + } + public String getOwner() { + return owner; + }*/ + /* + public void setGroup(String group) { + this.group = group; + // Set the flag + if(group!=null || owner!=null) + flags |= SSH_FILEXFER_ATTR_OWNERGROUP; + else + flags ^= SSH_FILEXFER_ATTR_OWNERGROUP; + } + public String getGroup() { + return group; + }*/ + public void setPermissions(UnsignedInteger32 permissions) { + this.permissions = permissions; + + // Set the flag + if (permissions != null) { + flags |= SSH_FILEXFER_ATTR_PERMISSIONS; + } else { + flags ^= SSH_FILEXFER_ATTR_PERMISSIONS; + } + } + + /** + * Set permissions given a UNIX style mask + * + * @param mask mask + * + * @throws IllegalArgumentException if badly formatted string + */ + public void setPermissionsFromMaskString(String mask) { + if (mask.length() != 4) { + throw new IllegalArgumentException("Mask length must be 4"); + } + + try { + setPermissions(new UnsignedInteger32(String.valueOf( + Integer.parseInt(mask, 8)))); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException( + "Mask must be 4 digit octal number."); + } + } + + /** + * + * + * @param newPermissions + */ + public void setPermissions(String newPermissions) { + int cp = 0; + + if (permissions != null) { + cp = cp | + (((permissions.intValue() & S_IFMT) == S_IFMT) ? S_IFMT : 0); + cp = cp | + (((permissions.intValue() & S_IFSOCK) == S_IFSOCK) ? S_IFSOCK : 0); + cp = cp | + (((permissions.intValue() & S_IFLNK) == S_IFLNK) ? S_IFLNK : 0); + cp = cp | + (((permissions.intValue() & S_IFREG) == S_IFREG) ? S_IFREG : 0); + cp = cp | + (((permissions.intValue() & S_IFBLK) == S_IFBLK) ? S_IFBLK : 0); + cp = cp | + (((permissions.intValue() & S_IFDIR) == S_IFDIR) ? S_IFDIR : 0); + cp = cp | + (((permissions.intValue() & S_IFCHR) == S_IFCHR) ? S_IFCHR : 0); + cp = cp | + (((permissions.intValue() & S_IFIFO) == S_IFIFO) ? S_IFIFO : 0); + cp = cp | + (((permissions.intValue() & S_ISUID) == S_ISUID) ? S_ISUID : 0); + cp = cp | + (((permissions.intValue() & S_ISGID) == S_ISGID) ? S_ISGID : 0); + } + + int len = newPermissions.length(); + + if (len >= 1) { + cp = cp | + ((newPermissions.charAt(0) == 'r') ? FileAttributes.S_IRUSR : 0); + } + + if (len >= 2) { + cp = cp | + ((newPermissions.charAt(1) == 'w') ? FileAttributes.S_IWUSR : 0); + } + + if (len >= 3) { + cp = cp | + ((newPermissions.charAt(2) == 'x') ? FileAttributes.S_IXUSR : 0); + } + + if (len >= 4) { + cp = cp | + ((newPermissions.charAt(3) == 'r') ? FileAttributes.S_IRGRP : 0); + } + + if (len >= 5) { + cp = cp | + ((newPermissions.charAt(4) == 'w') ? FileAttributes.S_IWGRP : 0); + } + + if (len >= 6) { + cp = cp | + ((newPermissions.charAt(5) == 'x') ? FileAttributes.S_IXGRP : 0); + } + + if (len >= 7) { + cp = cp | + ((newPermissions.charAt(6) == 'r') ? FileAttributes.S_IROTH : 0); + } + + if (len >= 8) { + cp = cp | + ((newPermissions.charAt(7) == 'w') ? FileAttributes.S_IWOTH : 0); + } + + if (len >= 9) { + cp = cp | + ((newPermissions.charAt(8) == 'x') ? FileAttributes.S_IXOTH : 0); + } + + setPermissions(new UnsignedInteger32(cp)); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getPermissions() { + return permissions; + } + + /** + * + * + * @param atime + * @param mtime + */ + public void setTimes(UnsignedInteger32 atime, UnsignedInteger32 mtime) { + this.atime = atime; + this.mtime = mtime; + + // Set the flag + if (atime != null) { + flags |= SSH_FILEXFER_ATTR_ACCESSTIME; + } else { + flags ^= SSH_FILEXFER_ATTR_ACCESSTIME; + } + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getAccessedTime() { + return atime; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getModifiedTime() { + if (mtime != null) { + return mtime; + } else { + return new UnsignedInteger32(0); + } + } + + /* + public void setCreatedTime(UnsignedInteger32 atime) { + this.createtime = createtime; + // Set the flag + if(createtime!=null) + flags |= SSH_FILEXFER_ATTR_CREATETIME; + else + flags ^= SSH_FILEXFER_ATTR_CREATETIME; + } + public UnsignedInteger32 getCreatedTime() { + return createtime; + } + public void setModifiedTime(UnsignedInteger32 atime) { + this.mtime = mtime; + // Set the flag + if(mtime!=null) + flags |= SSH_FILEXFER_ATTR_MODIFYTIME; + else + flags ^= SSH_FILEXFER_ATTR_MODIFYTIME; + } + public UnsignedInteger32 getModifiedTime() { + return mtime; + }*/ + /* + public List getACL() { + return acl; + }*/ + public Map getExtendedAttributes() { + return extended; + } + + /** + * + * + * @param flag + * + * @return + */ + public boolean isFlagSet(int flag) { + return ((flags & flag) == flag); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public byte[] toByteArray() throws IOException { + ByteArrayWriter baw = new ByteArrayWriter(); + + if (extended.size() > 0) { + flags |= SSH_FILEXFER_ATTR_EXTENDED; + + /* + if(acl.size() > 0) + flags |= SSH_FILEXFER_ATTR_ACL; + */ + } + + baw.writeInt(flags); + + /* + baw.write(type); + */ + + if (isFlagSet(SSH_FILEXFER_ATTR_SIZE)) { + baw.writeUINT64(size); + } + + if (isFlagSet(SSH_FILEXFER_ATTR_UIDGID)) { + if (uid != null) { + baw.writeUINT32(uid); + } else { + baw.writeInt(0); + } + + if (gid != null) { + baw.writeUINT32(gid); + } else { + baw.writeInt(0); + } + } + + /* + if(isFlagSet(SSH_FILEXFER_ATTR_OWNERGROUP)) { + baw.writeString(owner); + baw.writeString(group); + } + */ + if (isFlagSet(SSH_FILEXFER_ATTR_PERMISSIONS)) { + baw.writeUINT32(permissions); + } + + if (isFlagSet(SSH_FILEXFER_ATTR_ACCESSTIME)) { + baw.writeUINT32(atime); + baw.writeUINT32(mtime); + } + + /* + if(isFlagSet(SSH_FILEXFER_ATTR_CREATETIME)) + baw.writeUINT32(createtime); + if(isFlagSet(SSH_FILEXFER_ATTR_MODIFYTIME)) + baw.writeUINT32(mtime); + if(isFlagSet(SSH_FILEXFER_ATTR_ACL)) { + ByteArrayWriter acls = new ByteArrayWriter(); + acls.writeInt(acl.size()); + Iterator it = acl.iterator(); + while(it.hasNext()) { + acls.write(((ACL)it.next()).toByteArray()); + } + baw.writeBinaryString(acls.toByteArray()); + acls = null; + }*/ + if (isFlagSet(SSH_FILEXFER_ATTR_EXTENDED)) { + baw.writeInt(extended.size()); + + Iterator it = extended.entrySet().iterator(); + Set set; + + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + baw.writeString((String) entry.getKey()); + baw.writeString((String) entry.getValue()); + } + } + + return baw.toByteArray(); + } + + private int octal(int v, int r) { + v >>>= r; + + return (((v & 0x04) != 0) ? 4 : 0) + (((v & 0x02) != 0) ? 2 : 0) + + +(((v & 0x01) != 0) ? 1 : 0); + } + + private String rwxString(int v, int r) { + v >>>= r; + + String rwx = ((((v & 0x04) != 0) ? "r" : "-") + + (((v & 0x02) != 0) ? "w" : "-")); + + if (((r == 6) && ((permissions.intValue() & S_ISUID) == S_ISUID)) || + ((r == 3) && ((permissions.intValue() & S_ISGID) == S_ISGID))) { + rwx += (((v & 0x01) != 0) ? "s" : "S"); + } else { + rwx += (((v & 0x01) != 0) ? "x" : "-"); + } + + return rwx; + } + + /** + * + * + * @return + */ + public String getPermissionsString() { + if (permissions != null) { + StringBuffer str = new StringBuffer(); + str.append(types[(permissions.intValue() & S_IFMT) >>> 13]); + str.append(rwxString(permissions.intValue(), 6)); + str.append(rwxString(permissions.intValue(), 3)); + str.append(rwxString(permissions.intValue(), 0)); + + return str.toString(); + } else { + return ""; + } + } + + /** + * Return the UNIX style mode mask + * + * @return mask + */ + public String getMaskString() { + StringBuffer buf = new StringBuffer(); + buf.append('0'); + + int i = permissions.intValue(); + buf.append(octal(i, 6)); + buf.append(octal(i, 3)); + buf.append(octal(i, 0)); + + return buf.toString(); + } + + /** + * + * + * @return + */ + public String getModTimeString() { + if (mtime == null) { + return ""; + } + + SimpleDateFormat df; + long mt = (mtime.longValue() * 1000L); + long now = System.currentTimeMillis(); + + if ((now - mt) > (6 * 30 * 24 * 60 * 60 * 1000L)) { + df = new SimpleDateFormat("MMM dd yyyy"); + } else { + df = new SimpleDateFormat("MMM dd hh:mm"); + } + + return df.format(new Date(mt)); + } + + /** + * + * + * @return + */ + public boolean isDirectory() { + if (permissions == null) { + return false; + } + else { + return ( ( permissions.intValue() & S_IFMT ) == S_IFDIR ); + } + } + + /** + * + * + * @return + */ + public boolean isFile() { + if (permissions == null) { + return false; + } else { + return (permissions.intValue() & S_IFMT ) == S_IFREG; + } + } + + /** + * + * + * @return + */ + public boolean isLink() { + if (permissions == null) { + return false; + } else { + return (permissions.intValue() & S_IFMT) == S_IFLNK; + } + } + + /** + * + * + * @return + */ + public boolean isFifo() { + if (permissions == null) { + return false; + } else { + return (permissions.intValue() & S_IFMT) == S_IFIFO; + } + } + + /** + * + * + * @return + */ + public boolean isBlock() { + if (permissions == null) { + return false; + } else { + return (permissions.intValue() & S_IFMT) == S_IFBLK; + } + } + + /** + * + * + * @return + */ + public boolean isCharacter() { + if (permissions == null) { + return false; + } else { + return (permissions.intValue() & S_IFMT) == S_IFCHR; + } + } + + /** + * + * + * @return + */ + public boolean isSocket() { + if (permissions == null) { + return false; + } else { + return (permissions.intValue() & S_IFMT) == S_IFSOCK; + } + } +} diff --git a/src/com/sshtools/j2ssh/sftp/MessageRequestId.java b/src/com/sshtools/j2ssh/sftp/MessageRequestId.java new file mode 100644 index 0000000000000000000000000000000000000000..952a3a9849db05be44e1c9c3ae9cba9960f31a67 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/MessageRequestId.java @@ -0,0 +1,38 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.*; + + +interface MessageRequestId { + /** + * + * + * @return + */ + public UnsignedInteger32 getId(); +} diff --git a/src/com/sshtools/j2ssh/sftp/SftpFile.java b/src/com/sshtools/j2ssh/sftp/SftpFile.java new file mode 100644 index 0000000000000000000000000000000000000000..b6390defecee3353c63ac017c228a0c387074b40 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SftpFile.java @@ -0,0 +1,326 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.23 $ + */ +public class SftpFile implements Comparable { + private String filename; + private byte[] handle; + private FileAttributes attrs; + private SftpSubsystemClient sftp; + private String absolutePath; + + /** + * Creates a new SftpFile object. + * + * @param absolutePath + * @param attrs + */ + public SftpFile(String absolutePath, FileAttributes attrs) { + this.absolutePath = absolutePath; + + int i = absolutePath.lastIndexOf("/"); + + if (i > -1) { + this.filename = absolutePath.substring(i + 1); + } else { + this.filename = absolutePath; + } + + this.attrs = attrs; + } + + /** + * Creates a new SftpFile object. + * + * @param absolutePath + */ + public SftpFile(String absolutePath) { + this(absolutePath, new FileAttributes()); + } + + /** + * + * + * @throws IOException + */ + public void delete() throws IOException { + if (sftp == null) { + throw new IOException("Instance not connected to SFTP subsystem"); + } + + if (isDirectory()) { + sftp.removeDirectory(getAbsolutePath()); + } else { + sftp.removeFile(getAbsolutePath()); + } + } + + /** + * + * + * @param newFilename + * + * @throws IOException + */ + public void rename(String newFilename) throws IOException { + if (sftp == null) { + throw new IOException("Instance not connected to SFTP subsystem"); + } + + sftp.renameFile(getAbsolutePath() + filename, newFilename); + } + + /** + * + * + * @return + */ + public boolean canWrite() { + return (getAttributes().getPermissions().longValue() & + FileAttributes.S_IWUSR) == FileAttributes.S_IWUSR; + } + + /** + * + * + * @return + */ + public boolean canRead() { + return (getAttributes().getPermissions().longValue() & + FileAttributes.S_IRUSR) == FileAttributes.S_IRUSR; + } + + /** + * + * + * @return + */ + public boolean isOpen() { + if (sftp == null) { + return false; + } + + return sftp.isValidHandle(handle); + } + + /** + * + * + * @param handle + */ + protected void setHandle(byte[] handle) { + this.handle = handle; + } + + /** + * + * + * @return + */ + protected byte[] getHandle() { + return handle; + } + + /** + * + * + * @param sftp + */ + protected void setSFTPSubsystem(SftpSubsystemClient sftp) { + this.sftp = sftp; + } + + /** + * + * + * @return + */ + protected SftpSubsystemClient getSFTPSubsystem() { + return sftp; + } + + /** + * + * + * @return + */ + public String getFilename() { + return filename; + } + + private String pad(int num) { + String str = ""; + + if (num > 0) { + for (int i = 0; i < num; i++) { + str += " "; + } + } + + return str; + } + + /** + * + * + * @return + */ + public String getLongname() { + StringBuffer str = new StringBuffer(); + str.append(pad(10 - getAttributes().getPermissionsString().length()) + + getAttributes().getPermissionsString()); + str.append(" 1 "); + str.append(getAttributes().getUID().toString() + + pad(8 - getAttributes().getUID().toString().length())); //uid + str.append(" "); + str.append(getAttributes().getGID().toString() + + pad(8 - getAttributes().getGID().toString().length())); //gid + str.append(" "); + str.append(pad(8 - getAttributes().getSize().toString().length()) + + getAttributes().getSize().toString()); + str.append(" "); + str.append(pad(12 - getAttributes().getModTimeString().length()) + + getAttributes().getModTimeString()); + str.append(" "); + str.append(filename); + + return str.toString(); + } + + /** + * + * + * @return + */ + public FileAttributes getAttributes() { + try { + if (attrs == null) { + attrs = sftp.getAttributes(this); + } + } catch (IOException ioe) { + attrs = new FileAttributes(); + } + + return attrs; + } + + /** + * + * + * @return + */ + public String getAbsolutePath() { + return absolutePath; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + sftp.closeFile(this); + } + + /** + * + * + * @return + */ + public boolean isDirectory() { + return getAttributes().isDirectory(); + } + + /** + * + * + * @return + */ + public boolean isFile() { + return getAttributes().isFile(); + } + + /** + * + * + * @return + */ + public boolean isLink() { + return getAttributes().isLink(); + } + + /** + * + * + * @return + */ + public boolean isFifo() { + return getAttributes().isFifo(); + } + + /** + * + * + * @return + */ + public boolean isBlock() { + return getAttributes().isBlock(); + } + + /** + * + * + * @return + */ + public boolean isCharacter() { + return getAttributes().isCharacter(); + } + + /** + * + * + * @return + */ + public boolean isSocket() { + return getAttributes().isSocket(); + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object o) { + return getFilename().compareTo(((SftpFile) o).getFilename()); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SftpFileInputStream.java b/src/com/sshtools/j2ssh/sftp/SftpFileInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..c2630a189ced877e7c0468ada01fbe8634158bd9 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SftpFileInputStream.java @@ -0,0 +1,121 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SftpFileInputStream extends InputStream { + SftpFile file; + UnsignedInteger64 position = new UnsignedInteger64("0"); + + /** + * Creates a new SftpFileInputStream object. + * + * @param file + * + * @throws IOException + */ + public SftpFileInputStream(SftpFile file) throws IOException { + if (file.getHandle() == null) { + throw new IOException("The file does not have a valid handle!"); + } + + if (file.getSFTPSubsystem() == null) { + throw new IOException( + "The file is not attached to an SFTP subsystem!"); + } + + this.file = file; + } + + /** + * + * + * @param buffer + * @param offset + * @param len + * + * @return + * + * @throws IOException + */ + public int read(byte[] buffer, int offset, int len) + throws IOException { + int read = file.getSFTPSubsystem().readFile(file.getHandle(), position, + buffer, offset, len); + + if (read > 0) { + position = UnsignedInteger64.add(position, read); + } + + return read; + } + + /** + * + * + * @return + * + * @throws java.io.IOException + */ + public int read() throws java.io.IOException { + byte[] buffer = new byte[1]; + int read = file.getSFTPSubsystem().readFile(file.getHandle(), position, + buffer, 0, 1); + position = UnsignedInteger64.add(position, read); + + return buffer[0] & 0xFF; + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + file.close(); + } + + /** + * + * + * @throws IOException + */ + protected void finalize() throws IOException { + if (file.getHandle() != null) { + close(); + } + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SftpFileOutputStream.java b/src/com/sshtools/j2ssh/sftp/SftpFileOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..a9a271ddc73c5988c6442517f6271394e8b6e2ac --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SftpFileOutputStream.java @@ -0,0 +1,124 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.*; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SftpFileOutputStream extends OutputStream { + SftpFile file; + UnsignedInteger64 position = new UnsignedInteger64("0"); + + /** + * Creates a new SftpFileOutputStream object. + * + * @param file + * + * @throws IOException + */ + public SftpFileOutputStream(SftpFile file) throws IOException { + if (file.getHandle() == null) { + throw new IOException("The file does not have a valid handle!"); + } + + if (file.getSFTPSubsystem() == null) { + throw new IOException( + "The file is not attached to an SFTP subsystem!"); + } + + this.file = file; + } + + /** + * + * + * @param buffer + * @param offset + * @param len + * + * @throws IOException + */ + public void write(byte[] buffer, int offset, int len) + throws IOException { + int pos = 0; + int count; + int available; + int max = (int) file.getSFTPSubsystem().maximumPacketSize(); + + while (pos < len) { + available = ((int) file.getSFTPSubsystem().availableWindowSpace() < max) + ? (int) file.getSFTPSubsystem().availableWindowSpace() : max; + count = (available < (len - pos)) ? available : (len - pos); + file.getSFTPSubsystem().writeFile(file.getHandle(), position, + buffer, offset + pos, count); + position = UnsignedInteger64.add(position, count); + pos += count; + } + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void write(int b) throws IOException { + byte[] buffer = new byte[1]; + buffer[0] = (byte) b; + file.getSFTPSubsystem().writeFile(file.getHandle(), position, buffer, + 0, 1); + position = UnsignedInteger64.add(position, 1); + } + + /** + * + * + * @throws IOException + */ + public void close() throws IOException { + file.close(); + } + + /** + * + * + * @throws IOException + */ + protected void finalize() throws IOException { + if (file.getHandle() != null) { + close(); + } + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SftpMessageStore.java b/src/com/sshtools/j2ssh/sftp/SftpMessageStore.java new file mode 100644 index 0000000000000000000000000000000000000000..b9ee10aabf4b82088b4eed1559a7511c310b5f6b --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SftpMessageStore.java @@ -0,0 +1,87 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.subsystem.SubsystemMessageStore; +import com.sshtools.j2ssh.util.OpenClosedState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Iterator; + + +class SftpMessageStore extends SubsystemMessageStore { + /** */ + public static Log log = LogFactory.getLog(SftpMessageStore.class); + + /** + * Creates a new SftpMessageStore object. + */ + public SftpMessageStore() { + } + + /** + * + * + * @param requestId + * + * @return + * + * @throws InterruptedException + */ + public synchronized SubsystemMessage getMessage(UnsignedInteger32 requestId) + throws InterruptedException { + Iterator it; + SubsystemMessage msg; + + // If there ae no messages available then wait untill there are. + while (getState().getValue() == OpenClosedState.OPEN) { + if (messages.size() > 0) { + it = messages.iterator(); + + while (it.hasNext()) { + msg = (SubsystemMessage) it.next(); + + if (msg instanceof MessageRequestId) { + if (((MessageRequestId) msg).getId().equals(requestId)) { + messages.remove(msg); + + return msg; + } + } + } + } + + log.debug("Waiting for new messages"); + wait(5000); + } + + return null; + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SftpSubsystemClient.java b/src/com/sshtools/j2ssh/sftp/SftpSubsystemClient.java new file mode 100644 index 0000000000000000000000000000000000000000..4735198cd2fe7174f7b0dd4810a4487ee3d74d65 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SftpSubsystemClient.java @@ -0,0 +1,931 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.connection.ChannelState; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.io.UnsignedInteger64; +import com.sshtools.j2ssh.subsystem.SubsystemChannel; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.MessageNotAvailableException; +import com.sshtools.j2ssh.transport.MessageStoreEOFException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.List; +import java.util.StringTokenizer; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.38 $ + */ +public class SftpSubsystemClient extends SubsystemChannel { + /** */ + public static final int OPEN_READ = SshFxpOpen.FXF_READ; + + /** */ + public static final int OPEN_WRITE = SshFxpOpen.FXF_WRITE; + + /** */ + public static final int OPEN_APPEND = SshFxpOpen.FXF_APPEND; + + /** */ + public static final int OPEN_CREATE = SshFxpOpen.FXF_CREAT; + + /** */ + public static final int OPEN_TRUNCATE = SshFxpOpen.FXF_TRUNC; + + /** */ + public static final int OPEN_EXCLUSIVE = SshFxpOpen.FXF_EXCL; + + /** */ + public static final int VERSION_1 = 1; + + /** */ + public static final int VERSION_2 = 2; + + /** */ + public static final int VERSION_3 = 3; + + /** */ + public static final int VERSION_4 = 4; + + /* Private variables */ + private static Log log = LogFactory.getLog(SftpSubsystemClient.class); + private List handles = new Vector(); + private UnsignedInteger32 nextRequestId = new UnsignedInteger32(1); + private int version = VERSION_3; + private SftpMessageStore messageStore; + + /** + * Creates a new SftpSubsystemClient object. + */ + public SftpSubsystemClient() { + // We will use our own message store implementation + super("sftp", new SftpMessageStore()); + messageStore = (SftpMessageStore) super.messageStore; + registerMessages(); + } + + /** + * + * + * @return + */ + public String getName() { + return "sftp"; + } + + /** + * + * + * @return + */ + protected long availableWindowSpace() { + return getRemoteWindow().getWindowSpace(); + } + + /** + * + * + * @return + */ + protected long maximumPacketSize() { + return getRemotePacketSize(); + } + + /** + * + * + * @param handle + * + * @throws IOException + */ + protected synchronized void closeHandle(byte[] handle) + throws IOException { + if (!isValidHandle(handle)) { + throw new IOException("The handle is invalid!"); + } + + // We will remove the handle first so that even if an excpetion occurs + // the file as far as were concerned is closed + handles.remove(handle); + + UnsignedInteger32 requestId = nextRequestId(); + SshFxpClose msg = new SshFxpClose(requestId, handle); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param file + * + * @throws IOException + */ + public void closeFile(SftpFile file) throws IOException { + closeHandle(file.getHandle()); + } + + /** + * + * + * @param handle + * + * @return + */ + protected boolean isValidHandle(byte[] handle) { + return handles.contains(handle); + } + + /** + * + * + * @param file + * @param children + * + * @return + * + * @throws IOException + */ + public synchronized int listChildren(SftpFile file, List children) + throws IOException { + if (file.isDirectory()) { + if (!isValidHandle(file.getHandle())) { + file = openDirectory(file.getAbsolutePath()); + + if (!isValidHandle(file.getHandle())) { + throw new IOException("Failed to open directory"); + } + } + } else { + throw new IOException("Cannot list children for this file object"); + } + + UnsignedInteger32 requestId = nextRequestId(); + SshFxpReadDir msg = new SshFxpReadDir(requestId, file.getHandle()); + sendMessage(msg); + + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpName) { + SshFxpName names = (SshFxpName) reply; + SftpFile[] files = names.getFiles(); + SftpFile f; + + for (int i = 0; i < files.length; i++) { + f = new SftpFile(file.getAbsolutePath() + "/" + + files[i].getFilename(), files[i].getAttributes()); + f.setSFTPSubsystem(this); + children.add(f); + } + + return files.length; + } else if (reply instanceof SshFxpStatus) { + SshFxpStatus status = (SshFxpStatus) reply; + + if (status.getErrorCode().intValue() == SshFxpStatus.STATUS_FX_EOF) { + return -1; + } else { + throw new IOException(status.getErrorMessage()); + } + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + /** + * + * + * @param path + * + * @throws IOException + */ + public synchronized void makeDirectory(String path) + throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SshFxpMkdir msg = new SshFxpMkdir(requestId, path, new FileAttributes()); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param path + * + * @throws IOException + */ + public void recurseMakeDirectory(String path) throws IOException { + SftpFile file; + + if (path.trim().length() > 0) { + try { + file = openDirectory(path); + file.close(); + } catch (IOException ioe) { + StringTokenizer tokenizer = new StringTokenizer(path, "/", true); + String dir = ""; + + while (tokenizer.hasMoreElements()) { + dir += tokenizer.nextElement(); + + try { + file = openDirectory(dir); + file.close(); + } catch (IOException ioe2) { + log.info("Creating " + dir); + makeDirectory(dir); + } + } + } + } + } + + /** + * + * + * @return + * + * @throws IOException + */ + + /*protected boolean onStart() throws IOException { + return initialize(); + }*/ + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + */ + public synchronized SftpFile openDirectory(String path) + throws IOException { + String absolutePath = getAbsolutePath(path); + UnsignedInteger32 requestId = nextRequestId(); + SubsystemMessage msg = new SshFxpOpenDir(requestId, absolutePath); + sendMessage(msg); + + byte[] handle = getHandleResponse(requestId); + requestId = nextRequestId(); + msg = new SshFxpStat(requestId, absolutePath); + sendMessage(msg); + + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpAttrs) { + SftpFile file = new SftpFile(absolutePath, + ((SshFxpAttrs) reply).getAttributes()); + file.setHandle(handle); + file.setSFTPSubsystem(this); + + return file; + } else if (reply instanceof SshFxpStatus) { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + /** + * + * + * @return + * + * @throws IOException + */ + public String getDefaultDirectory() throws IOException { + return getAbsolutePath(""); + } + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + */ + public synchronized String getAbsolutePath(String path) + throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SubsystemMessage msg = new SshFxpRealPath(requestId, path); + sendMessage(msg); + + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpName) { + SftpFile[] files = ((SshFxpName) reply).getFiles(); + + if (files.length != 1) { + throw new IOException( + "Server responded to SSH_FXP_REALPATH with too many files!"); + } + + return files[0].getAbsolutePath(); + } else if (reply instanceof SshFxpStatus) { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + /** + * + * + * @param file + * + * @return + * + * @throws IOException + */ + public String getAbsolutePath(SftpFile file) throws IOException { + return getAbsolutePath(file.getFilename()); + } + + /** + * + * + * @param filename + * @param flags + * + * @return + * + * @throws IOException + */ + public SftpFile openFile(String filename, int flags) + throws IOException { + return openFile(filename, flags, null); + } + + /** + * + * + * @param absolutePath + * @param flags + * @param attrs + * + * @return + * + * @throws IOException + */ + public synchronized SftpFile openFile(String absolutePath, int flags, + FileAttributes attrs) throws IOException { + if (attrs == null) { + attrs = new FileAttributes(); + } + + UnsignedInteger32 requestId = nextRequestId(); + SubsystemMessage msg = new SshFxpOpen(requestId, absolutePath, + new UnsignedInteger32(flags), attrs); + sendMessage(msg); + + byte[] handle = getHandleResponse(requestId); + SftpFile file = new SftpFile(absolutePath, null); + file.setHandle(handle); + file.setSFTPSubsystem(this); + + return file; + } + + /** + * + * + * @param path + * + * @return + * + * @throws IOException + */ + public synchronized FileAttributes getAttributes(String path) + throws IOException { + SubsystemMessage msg; + UnsignedInteger32 requestId = nextRequestId(); + msg = new SshFxpStat(requestId, path); + sendMessage(msg); + + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpAttrs) { + return ((SshFxpAttrs) reply).getAttributes(); + } else if (reply instanceof SshFxpStatus) { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + /** + * + * + * @param file + * + * @return + * + * @throws IOException + */ + public synchronized FileAttributes getAttributes(SftpFile file) + throws IOException { + SubsystemMessage msg; + UnsignedInteger32 requestId = nextRequestId(); + + if (!isValidHandle(file.getHandle())) { + msg = new SshFxpStat(requestId, file.getAbsolutePath()); + } else { + msg = new SshFxpFStat(requestId, file.getHandle()); + } + + sendMessage(msg); + + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpAttrs) { + return ((SshFxpAttrs) reply).getAttributes(); + } else if (reply instanceof SshFxpStatus) { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + /** + * + * + * @param handle + * @param offset + * @param output + * @param off + * @param len + * + * @return + * + * @throws IOException + */ + protected synchronized int readFile(byte[] handle, + UnsignedInteger64 offset, byte[] output, int off, int len) + throws IOException { + if (!handles.contains(handle)) { + throw new IOException("The file handle is invalid!"); + } + + if ((output.length - off) < len) { + throw new IOException( + "Output array size is smaller than read length!"); + } + + UnsignedInteger32 requestId = nextRequestId(); + SshFxpRead msg = new SshFxpRead(requestId, handle, offset, + new UnsignedInteger32(len)); + sendMessage(msg); + + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpData) { + byte[] msgdata = ((SshFxpData) reply).getData(); + System.arraycopy(msgdata, 0, output, off, msgdata.length); + + return msgdata.length; + } else if (reply instanceof SshFxpStatus) { + SshFxpStatus status = (SshFxpStatus) reply; + + if (status.getErrorCode().intValue() == SshFxpStatus.STATUS_FX_EOF) { + return -1; + } else { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + /** + * + * + * @param path + * + * @throws IOException + */ + public synchronized void removeDirectory(String path) + throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SshFxpRmdir msg = new SshFxpRmdir(requestId, path); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param filename + * + * @throws IOException + */ + public synchronized void removeFile(String filename) + throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SshFxpRemove msg = new SshFxpRemove(requestId, filename); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param oldpath + * @param newpath + * + * @throws IOException + */ + public synchronized void renameFile(String oldpath, String newpath) + throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SshFxpRename msg = new SshFxpRename(requestId, oldpath, newpath); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param handle + * @param offset + * @param data + * @param off + * @param len + * + * @throws IOException + */ + protected synchronized void writeFile(byte[] handle, + UnsignedInteger64 offset, byte[] data, int off, int len) + throws IOException { + if (!handles.contains(handle)) { + throw new IOException("The handle is not valid!"); + } + + if ((data.length - off) < len) { + throw new IOException("Incorrect data array size!"); + } + + UnsignedInteger32 requestId = nextRequestId(); + SshFxpWrite msg = new SshFxpWrite(requestId, handle, offset, data, off, + len); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param targetpath + * @param linkpath + * + * @throws IOException + */ + public synchronized void createSymbolicLink(String targetpath, + String linkpath) throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SubsystemMessage msg = new SshFxpSymlink(requestId, targetpath, linkpath); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param linkpath + * + * @return + * + * @throws IOException + */ + public synchronized String getSymbolicLinkTarget(String linkpath) + throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SubsystemMessage msg = new SshFxpReadlink(requestId, linkpath); + sendMessage(msg); + + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpName) { + SftpFile[] files = ((SshFxpName) reply).getFiles(); + + if (files.length != 1) { + throw new IOException( + "Server responded to SSH_FXP_REALLINK with too many files!"); + } + + return files[0].getAbsolutePath(); + } else if (reply instanceof SshFxpStatus) { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + /** + * + * + * @param path + * @param attrs + * + * @throws IOException + */ + public synchronized void setAttributes(String path, FileAttributes attrs) + throws IOException { + UnsignedInteger32 requestId = nextRequestId(); + SubsystemMessage msg = new SshFxpSetStat(requestId, path, attrs); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param file + * @param attrs + * + * @throws IOException + */ + public synchronized void setAttributes(SftpFile file, FileAttributes attrs) + throws IOException { + if (!isValidHandle(file.getHandle())) { + throw new IOException("The handle is not an open file handle!"); + } + + UnsignedInteger32 requestId = nextRequestId(); + SubsystemMessage msg = new SshFxpFSetStat(requestId, file.getHandle(), + attrs); + sendMessage(msg); + getOKRequestStatus(requestId); + } + + /** + * + * + * @param file + * @param permissions + * + * @throws IOException + */ + public void changePermissions(SftpFile file, String permissions) + throws IOException { + FileAttributes attrs = new FileAttributes(); //file.getAttributes(); + attrs.setPermissions(permissions); + setAttributes(file, attrs); + } + + /** + * + * + * @param file + * @param permissions + * + * @throws IOException + */ + public void changePermissions(SftpFile file, int permissions) + throws IOException { + FileAttributes attrs = new FileAttributes(); //file.getAttributes(); + attrs.setPermissions(new UnsignedInteger32(permissions)); + setAttributes(file, attrs); + } + + /** + * + * + * @param filename + * @param permissions + * + * @throws IOException + */ + public void changePermissions(String filename, int permissions) + throws IOException { + FileAttributes attrs = new FileAttributes(); + attrs.setPermissions(new UnsignedInteger32(permissions)); + setAttributes(filename, attrs); + } + + /** + * + * + * @param filename + * @param permissions + * + * @throws IOException + */ + public void changePermissions(String filename, String permissions) + throws IOException { + FileAttributes attrs = new FileAttributes(); + attrs.setPermissions(permissions); + setAttributes(filename, attrs); + } + + /** + * + * + * @return + * + * @throws IOException + */ + public synchronized boolean initialize() throws IOException { + log.info("Initializing SFTP protocol version " + + String.valueOf(version)); + + if (!startSubsystem()) { + return false; + } + + boolean result = false; + SshFxpInit msg = new SshFxpInit(new UnsignedInteger32(version), null); + sendMessage(msg); + + // Lets give the sftp subsystem 30 seconds to reply + SubsystemMessage reply = null; + + for (int i = 0; i < 30; i++) { + try { + reply = messageStore.nextMessage(1000); + + break; + } catch (MessageNotAvailableException ex) { + // We timed out so just continue by looking at the session state + } catch (MessageStoreEOFException ex) { + return false; + } + + if (getState().getValue() != ChannelState.CHANNEL_OPEN) { + return false; + } + + // Try again + } + + if (reply instanceof SshFxpVersion) { + result = true; + version = ((SshFxpVersion) reply).getVersion().intValue(); + log.info("Server responded with version " + + String.valueOf(version)); + } + + return result; + } + + private byte[] getHandleResponse(UnsignedInteger32 requestId) + throws IOException { + try { + SubsystemMessage reply = messageStore.getMessage(requestId); + + if (reply instanceof SshFxpHandle) { + byte[] handle = ((SshFxpHandle) reply).getHandle(); + + // Add the handle to our managed list + handles.add(handle); + + return handle; + } else if (reply instanceof SshFxpStatus) { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + private void getOKRequestStatus(UnsignedInteger32 requestId) + throws IOException { + try { + if (log.isDebugEnabled()) { + log.info("Waiting for response"); + } + + SubsystemMessage reply = messageStore.getMessage(requestId); + log.info("Received response"); + + if (reply instanceof SshFxpStatus) { + SshFxpStatus status = (SshFxpStatus) reply; + + if (status.getErrorCode().intValue() != SshFxpStatus.STATUS_FX_OK) { + throw new IOException(((SshFxpStatus) reply).getErrorMessage()); + } + } else { + throw new IOException("Unexpected server response " + + reply.getMessageName()); + } + } catch (InterruptedException ex) { + throw new IOException("The thread was interrupted"); + } + } + + private UnsignedInteger32 nextRequestId() { + nextRequestId = UnsignedInteger32.add(nextRequestId, 1); + + return nextRequestId; + } + + private void registerMessages() { + messageStore.registerMessage(SshFxpVersion.SSH_FXP_VERSION, + SshFxpVersion.class); + messageStore.registerMessage(SshFxpAttrs.SSH_FXP_ATTRS, + SshFxpAttrs.class); + messageStore.registerMessage(SshFxpData.SSH_FXP_DATA, SshFxpData.class); + messageStore.registerMessage(SshFxpHandle.SSH_FXP_HANDLE, + SshFxpHandle.class); + messageStore.registerMessage(SshFxpStatus.SSH_FXP_STATUS, + SshFxpStatus.class); + messageStore.registerMessage(SshFxpName.SSH_FXP_NAME, SshFxpName.class); + } + + protected int getMinimumWindowSpace() { + return 1024; + } + + /** + * + * + * @return + */ + protected int getMaximumWindowSpace() { + return 131070; + } + + /** + * + * + * @return + */ + protected int getMaximumPacketSize() { + return 65535; + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpAttrs.java b/src/com/sshtools/j2ssh/sftp/SshFxpAttrs.java new file mode 100644 index 0000000000000000000000000000000000000000..1d4c61f8ca52e8000ff2bbb81bd2dce28b228e44 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpAttrs.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpAttrs extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_ATTRS = 105; + private UnsignedInteger32 id; + private FileAttributes attrs; + + /** + * Creates a new SshFxpAttrs object. + */ + public SshFxpAttrs() { + super(SSH_FXP_ATTRS); + } + + /** + * Creates a new SshFxpAttrs object. + * + * @param id + * @param attrs + */ + public SshFxpAttrs(UnsignedInteger32 id, FileAttributes attrs) { + super(SSH_FXP_ATTRS); + this.id = id; + this.attrs = attrs; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public FileAttributes getAttributes() { + return attrs; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + attrs = new FileAttributes(bar); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_ATTRS"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.write(attrs.toByteArray()); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpClose.java b/src/com/sshtools/j2ssh/sftp/SshFxpClose.java new file mode 100644 index 0000000000000000000000000000000000000000..18dd9c1796b2d526054e85d2bc40ea3dad817946 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpClose.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshFxpClose extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_CLOSE = 4; + private UnsignedInteger32 id; + private byte[] handle; + + /** + * Creates a new SshFxpClose object. + */ + public SshFxpClose() { + super(SSH_FXP_CLOSE); + } + + /** + * Creates a new SshFxpClose object. + * + * @param id + * @param handle + */ + public SshFxpClose(UnsignedInteger32 id, byte[] handle) { + super(SSH_FXP_CLOSE); + this.id = id; + this.handle = handle; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getHandle() { + return handle; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + handle = bar.readBinaryString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_CLOSE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(handle); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpData.java b/src/com/sshtools/j2ssh/sftp/SshFxpData.java new file mode 100644 index 0000000000000000000000000000000000000000..bf306678e08f7014ac8992c0061c17da68855dc8 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpData.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpData extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_DATA = 103; + private UnsignedInteger32 id; + private byte[] data; + + /** + * Creates a new SshFxpData object. + * + * @param id + * @param data + */ + public SshFxpData(UnsignedInteger32 id, byte[] data) { + super(SSH_FXP_DATA); + this.id = id; + this.data = data; + } + + /** + * Creates a new SshFxpData object. + */ + public SshFxpData() { + super(SSH_FXP_DATA); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getData() { + return data; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + data = bar.readBinaryString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_DATA"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(data); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpFSetStat.java b/src/com/sshtools/j2ssh/sftp/SshFxpFSetStat.java new file mode 100644 index 0000000000000000000000000000000000000000..87572545b8f13fc67313fbd3d091acb5963e5010 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpFSetStat.java @@ -0,0 +1,138 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpFSetStat extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_FSETSTAT = 10; + private UnsignedInteger32 id; + private byte[] handle; + private FileAttributes attrs; + + /** + * Creates a new SshFxpFSetStat object. + */ + public SshFxpFSetStat() { + super(SSH_FXP_FSETSTAT); + } + + /** + * Creates a new SshFxpFSetStat object. + * + * @param id + * @param handle + * @param attrs + */ + public SshFxpFSetStat(UnsignedInteger32 id, byte[] handle, + FileAttributes attrs) { + super(SSH_FXP_FSETSTAT); + this.id = id; + this.handle = handle; + this.attrs = attrs; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getHandle() { + return handle; + } + + /** + * + * + * @return + */ + public FileAttributes getAttributes() { + return attrs; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + handle = bar.readBinaryString(); + attrs = new FileAttributes(bar); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_FSETSTAT"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(handle); + baw.write(attrs.toByteArray()); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpFStat.java b/src/com/sshtools/j2ssh/sftp/SshFxpFStat.java new file mode 100644 index 0000000000000000000000000000000000000000..dc1d16351f81bd3b8d1f6a0b0fe820a1e362685f --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpFStat.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpFStat extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_FSTAT = 8; + private UnsignedInteger32 id; + private byte[] handle; + + /** + * Creates a new SshFxpFStat object. + */ + public SshFxpFStat() { + super(SSH_FXP_FSTAT); + } + + /** + * Creates a new SshFxpFStat object. + * + * @param id + * @param handle + */ + public SshFxpFStat(UnsignedInteger32 id, byte[] handle) { + super(SSH_FXP_FSTAT); + this.id = id; + this.handle = handle; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + handle = bar.readBinaryString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_FSTAT"; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getHandle() { + return handle; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(handle); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpHandle.java b/src/com/sshtools/j2ssh/sftp/SshFxpHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..1215f3466668fb1c5d52ca064b8b6ad962707a0f --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpHandle.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshFxpHandle extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_HANDLE = 102; + private UnsignedInteger32 id; + private byte[] handle; + + /** + * Creates a new SshFxpHandle object. + * + * @param id + * @param handle + */ + public SshFxpHandle(UnsignedInteger32 id, byte[] handle) { + super(SSH_FXP_HANDLE); + this.id = id; + this.handle = handle; + } + + /** + * Creates a new SshFxpHandle object. + */ + public SshFxpHandle() { + super(SSH_FXP_HANDLE); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getHandle() { + return handle; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + handle = bar.readBinaryString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_HANDLE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(handle); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpInit.java b/src/com/sshtools/j2ssh/sftp/SshFxpInit.java new file mode 100644 index 0000000000000000000000000000000000000000..593b1cbe340908fde8c1ce724cd45f6d8f1e0c30 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpInit.java @@ -0,0 +1,147 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpInit extends SubsystemMessage { + /** */ + public static final int SSH_FXP_INIT = 1; + private UnsignedInteger32 version; + private Map extended; + + /** + * Creates a new SshFxpInit object. + */ + public SshFxpInit() { + super(SSH_FXP_INIT); + } + + /** + * Creates a new SshFxpInit object. + * + * @param version + * @param extended + */ + public SshFxpInit(UnsignedInteger32 version, Map extended) { + super(SSH_FXP_INIT); + this.version = version; + this.extended = extended; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getVersion() { + return version; + } + + /** + * + * + * @return + */ + public Map getExtended() { + return extended; + } + + /** + * + * + * @param bar + * + * @throws IOException + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws IOException, InvalidMessageException { + version = bar.readUINT32(); + extended = new HashMap(); + + String key; + String value; + + while (bar.available() > 0) { + key = bar.readString(); + value = bar.readString(); + extended.put(key, value); + } + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_INIT"; + } + + /** + * + * + * @param baw + * + * @throws IOException + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws IOException, InvalidMessageException { + baw.writeUINT32(version); + + if (extended != null) { + if (extended.size() > 0) { + Iterator it = extended.entrySet().iterator(); + Map.Entry entry; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + baw.writeString((String) entry.getKey()); + baw.writeString((String) entry.getValue()); + } + } + } + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpLStat.java b/src/com/sshtools/j2ssh/sftp/SshFxpLStat.java new file mode 100644 index 0000000000000000000000000000000000000000..70a8c6b6530a7fb43f7d68acb4c301c569efebfb --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpLStat.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpLStat extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_LSTAT = 7; + private UnsignedInteger32 id; + private String path; + + /** + * Creates a new SshFxpLStat object. + */ + public SshFxpLStat() { + super(SSH_FXP_LSTAT); + } + + /** + * Creates a new SshFxpLStat object. + * + * @param id + * @param path + */ + public SshFxpLStat(UnsignedInteger32 id, String path) { + super(SSH_FXP_LSTAT); + this.id = id; + this.path = path; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_LSTAT"; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpMkdir.java b/src/com/sshtools/j2ssh/sftp/SshFxpMkdir.java new file mode 100644 index 0000000000000000000000000000000000000000..bb3efda8778b0d23382cf9586b1a6652a73ab043 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpMkdir.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpMkdir extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_MKDIR = 14; + private UnsignedInteger32 id; + private String path; + private FileAttributes attrs; + + /** + * Creates a new SshFxpMkdir object. + */ + public SshFxpMkdir() { + super(SSH_FXP_MKDIR); + } + + /** + * Creates a new SshFxpMkdir object. + * + * @param id + * @param path + * @param attrs + */ + public SshFxpMkdir(UnsignedInteger32 id, String path, FileAttributes attrs) { + super(SSH_FXP_MKDIR); + this.id = id; + this.path = path; + this.attrs = attrs; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @return + */ + public FileAttributes getAttributes() { + return attrs; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + attrs = new FileAttributes(bar); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_MKDIR"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + baw.write(attrs.toByteArray()); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpName.java b/src/com/sshtools/j2ssh/sftp/SshFxpName.java new file mode 100644 index 0000000000000000000000000000000000000000..92fd2bf25c85975db13efccb5233b7ddb58ec854 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpName.java @@ -0,0 +1,142 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshFxpName extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_NAME = 104; + private UnsignedInteger32 id; + private SftpFile[] files; + + /** + * Creates a new SshFxpName object. + * + * @param id + * @param files + */ + public SshFxpName(UnsignedInteger32 id, SftpFile[] files) { + super(SSH_FXP_NAME); + this.id = id; + this.files = files; + } + + /** + * Creates a new SshFxpName object. + */ + public SshFxpName() { + super(SSH_FXP_NAME); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public SftpFile[] getFiles() { + return files; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + + UnsignedInteger32 count = bar.readUINT32(); + files = new SftpFile[count.intValue()]; + + String shortname; + String longname; + + for (int i = 0; i < files.length; i++) { + shortname = bar.readString(); + longname = bar.readString(); + files[i] = new SftpFile(shortname, new FileAttributes(bar)); + } + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_NAME"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeUINT32(new UnsignedInteger32(files.length)); + + SftpFile file; + + for (int i = 0; i < files.length; i++) { + baw.writeString(files[i].getAbsolutePath()); + baw.writeString(files[i].getLongname()); + baw.write(files[i].getAttributes().toByteArray()); + } + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpOpen.java b/src/com/sshtools/j2ssh/sftp/SshFxpOpen.java new file mode 100644 index 0000000000000000000000000000000000000000..a9555c781f6ddbbde04ee7b6bc0613974381c3e8 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpOpen.java @@ -0,0 +1,162 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpOpen extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_OPEN = 3; + + /** */ + public static final int FXF_READ = 0x00000001; + + /** */ + public static final int FXF_WRITE = 0x00000002; + + /** */ + public static final int FXF_APPEND = 0x00000004; + + /** */ + public static final int FXF_CREAT = 0x00000008; + + /** */ + public static final int FXF_TRUNC = 0x00000010; + + /** */ + public static final int FXF_EXCL = 0x00000020; + private UnsignedInteger32 id; + private String filename; + private UnsignedInteger32 pflags; + private FileAttributes attrs; + + //public static final int FXF_TEXT = 0x00000040; + public SshFxpOpen(UnsignedInteger32 id, String filename, + UnsignedInteger32 pflags, FileAttributes attrs) { + super(SSH_FXP_OPEN); + this.id = id; + this.filename = filename; + this.pflags = pflags; + this.attrs = attrs; + } + + /** + * Creates a new SshFxpOpen object. + */ + public SshFxpOpen() { + super(SSH_FXP_OPEN); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getFilename() { + return filename; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getPflags() { + return pflags; + } + + /** + * + * + * @return + */ + public FileAttributes getAttributes() { + return attrs; + } + + /** + * + * + * @param bar + * + * @throws IOException + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws IOException, InvalidMessageException { + id = bar.readUINT32(); + filename = bar.readString(); + pflags = bar.readUINT32(); + attrs = new FileAttributes(bar); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_OPEN"; + } + + /** + * + * + * @param baw + * + * @throws IOException + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws IOException, InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(filename); + baw.writeUINT32(pflags); + baw.writeBinaryString(attrs.toByteArray()); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpOpenDir.java b/src/com/sshtools/j2ssh/sftp/SshFxpOpenDir.java new file mode 100644 index 0000000000000000000000000000000000000000..23ab70665b78ac9c23cf923e50c2229ce40f8483 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpOpenDir.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpOpenDir extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_OPENDIR = 11; + private UnsignedInteger32 id; + private String path; + + /** + * Creates a new SshFxpOpenDir object. + */ + public SshFxpOpenDir() { + super(SSH_FXP_OPENDIR); + } + + /** + * Creates a new SshFxpOpenDir object. + * + * @param id + * @param path + */ + public SshFxpOpenDir(UnsignedInteger32 id, String path) { + super(SSH_FXP_OPENDIR); + this.id = id; + this.path = path; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_OPEDIR"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpRead.java b/src/com/sshtools/j2ssh/sftp/SshFxpRead.java new file mode 100644 index 0000000000000000000000000000000000000000..c39e34dc4ae15b121cd20204b8bda6b713de3d32 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpRead.java @@ -0,0 +1,153 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.io.UnsignedInteger64; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshFxpRead extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_READ = 5; + private UnsignedInteger32 id; + private byte[] handle; + private UnsignedInteger64 offset; + private UnsignedInteger32 length; + + /** + * Creates a new SshFxpRead object. + * + * @param id + * @param handle + * @param offset + * @param length + */ + public SshFxpRead(UnsignedInteger32 id, byte[] handle, + UnsignedInteger64 offset, UnsignedInteger32 length) { + super(SSH_FXP_READ); + this.id = id; + this.handle = handle; + this.offset = offset; + this.length = length; + } + + /** + * Creates a new SshFxpRead object. + */ + public SshFxpRead() { + super(SSH_FXP_READ); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getHandle() { + return handle; + } + + /** + * + * + * @return + */ + public UnsignedInteger64 getOffset() { + return offset; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getLength() { + return length; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + handle = bar.readBinaryString(); + offset = bar.readUINT64(); + length = bar.readUINT32(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_READ"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(handle); + baw.writeUINT64(offset); + baw.writeUINT32(length); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpReadDir.java b/src/com/sshtools/j2ssh/sftp/SshFxpReadDir.java new file mode 100644 index 0000000000000000000000000000000000000000..adac68e4d29b6466d36da4553e4c19bcd0a1573f --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpReadDir.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpReadDir extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_READDIR = 12; + private UnsignedInteger32 id; + private byte[] handle; + + /** + * Creates a new SshFxpReadDir object. + */ + public SshFxpReadDir() { + super(SSH_FXP_READDIR); + } + + /** + * Creates a new SshFxpReadDir object. + * + * @param id + * @param handle + */ + public SshFxpReadDir(UnsignedInteger32 id, byte[] handle) { + super(SSH_FXP_READDIR); + this.id = id; + this.handle = handle; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getHandle() { + return handle; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + handle = bar.readBinaryString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_READDIR"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(handle); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpReadlink.java b/src/com/sshtools/j2ssh/sftp/SshFxpReadlink.java new file mode 100644 index 0000000000000000000000000000000000000000..6563c3969a4d8983458c00fe487a47328cf1d391 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpReadlink.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpReadlink extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_READLINK = 19; + private UnsignedInteger32 id; + private String path; + + /** + * Creates a new SshFxpReadlink object. + * + * @param id + * @param path + */ + public SshFxpReadlink(UnsignedInteger32 id, String path) { + super(SSH_FXP_READLINK); + this.id = id; + this.path = path; + } + + /** + * Creates a new SshFxpReadlink object. + */ + public SshFxpReadlink() { + super(SSH_FXP_READLINK); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_READLINK"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpRealPath.java b/src/com/sshtools/j2ssh/sftp/SshFxpRealPath.java new file mode 100644 index 0000000000000000000000000000000000000000..fd5d0658c6ff8243520fa0e3d1052e4498c5843d --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpRealPath.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpRealPath extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_REALPATH = 16; + private UnsignedInteger32 id; + private String path; + + /** + * Creates a new SshFxpRealPath object. + */ + public SshFxpRealPath() { + super(SSH_FXP_REALPATH); + } + + /** + * Creates a new SshFxpRealPath object. + * + * @param id + * @param path + */ + public SshFxpRealPath(UnsignedInteger32 id, String path) { + super(SSH_FXP_REALPATH); + this.id = id; + this.path = path; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_READPATH"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpRemove.java b/src/com/sshtools/j2ssh/sftp/SshFxpRemove.java new file mode 100644 index 0000000000000000000000000000000000000000..e65e350e89cf923358682e5356992ddc7a84344b --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpRemove.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpRemove extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_REMOVE = 13; + private UnsignedInteger32 id; + private String filename; + + /** + * Creates a new SshFxpRemove object. + */ + public SshFxpRemove() { + super(SSH_FXP_REMOVE); + } + + /** + * Creates a new SshFxpRemove object. + * + * @param id + * @param filename + */ + public SshFxpRemove(UnsignedInteger32 id, String filename) { + super(SSH_FXP_REMOVE); + this.id = id; + this.filename = filename; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getFilename() { + return filename; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + filename = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_REMOVE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(filename); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpRename.java b/src/com/sshtools/j2ssh/sftp/SshFxpRename.java new file mode 100644 index 0000000000000000000000000000000000000000..cb3e6f773a38bd97490be92a2ff1e3904e2a1f5d --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpRename.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpRename extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_RENAME = 18; + private UnsignedInteger32 id; + String oldpath; + String newpath; + + /** + * Creates a new SshFxpRename object. + */ + public SshFxpRename() { + super(SSH_FXP_RENAME); + } + + /** + * Creates a new SshFxpRename object. + * + * @param id + * @param oldpath + * @param newpath + */ + public SshFxpRename(UnsignedInteger32 id, String oldpath, String newpath) { + super(SSH_FXP_RENAME); + this.id = id; + this.oldpath = oldpath; + this.newpath = newpath; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getOldPath() { + return oldpath; + } + + /** + * + * + * @return + */ + public String getNewPath() { + return newpath; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + oldpath = bar.readString(); + newpath = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_RENAME"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(oldpath); + baw.writeString(newpath); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpRmdir.java b/src/com/sshtools/j2ssh/sftp/SshFxpRmdir.java new file mode 100644 index 0000000000000000000000000000000000000000..7542e2a65a43b8975d84d01f8ac833a65f60bff3 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpRmdir.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpRmdir extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_RMDIR = 15; + private UnsignedInteger32 id; + private String path; + + /** + * Creates a new SshFxpRmdir object. + */ + public SshFxpRmdir() { + super(SSH_FXP_RMDIR); + } + + /** + * Creates a new SshFxpRmdir object. + * + * @param id + * @param path + */ + public SshFxpRmdir(UnsignedInteger32 id, String path) { + super(SSH_FXP_RMDIR); + this.id = id; + this.path = path; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_RMDIR"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpSetStat.java b/src/com/sshtools/j2ssh/sftp/SshFxpSetStat.java new file mode 100644 index 0000000000000000000000000000000000000000..df56969312836ebce072e895bbd5e433b14df7ce --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpSetStat.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpSetStat extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_SETSTAT = 9; + private UnsignedInteger32 id; + private String path; + private FileAttributes attrs; + + /** + * Creates a new SshFxpSetStat object. + */ + public SshFxpSetStat() { + super(SSH_FXP_SETSTAT); + } + + /** + * Creates a new SshFxpSetStat object. + * + * @param id + * @param path + * @param attrs + */ + public SshFxpSetStat(UnsignedInteger32 id, String path, FileAttributes attrs) { + super(SSH_FXP_SETSTAT); + this.id = id; + this.path = path; + this.attrs = attrs; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @return + */ + public FileAttributes getAttributes() { + return attrs; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + attrs = new FileAttributes(bar); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_SETSTAT"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + baw.write(attrs.toByteArray()); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpStat.java b/src/com/sshtools/j2ssh/sftp/SshFxpStat.java new file mode 100644 index 0000000000000000000000000000000000000000..7f9934ebbd8a3ba91ce016223ad8e8035a4299b5 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpStat.java @@ -0,0 +1,123 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpStat extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_STAT = 17; + private UnsignedInteger32 id; + private String path; + + /** + * Creates a new SshFxpStat object. + */ + public SshFxpStat() { + super(SSH_FXP_STAT); + } + + /** + * Creates a new SshFxpStat object. + * + * @param id + * @param path + */ + public SshFxpStat(UnsignedInteger32 id, String path) { + super(SSH_FXP_STAT); + this.id = id; + this.path = path; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + path = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_STAT"; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getPath() { + return path; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(path); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpStatus.java b/src/com/sshtools/j2ssh/sftp/SshFxpStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..f3ba4e93ac2155e2918a3b345671693c984a329e --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpStatus.java @@ -0,0 +1,184 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public class SshFxpStatus extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_STATUS = 101; + + /** */ + public static final int STATUS_FX_OK = 0; + + /** */ + public static final int STATUS_FX_EOF = 1; + + /** */ + public static final int STATUS_FX_NO_SUCH_FILE = 2; + + /** */ + public static final int STATUS_FX_PERMISSION_DENIED = 3; + + /** */ + public static final int STATUS_FX_FAILURE = 4; + + /** */ + public static final int STATUS_FX_BAD_MESSAGE = 5; + + /** */ + public static final int STATUS_FX_NO_CONNECTION = 6; + + /** */ + public static final int STATUS_FX_CONNECTION_LOST = 7; + + /** */ + public static final int STATUS_FX_OP_UNSUPPORTED = 8; + + //public static final int STATUS_FX_INVALID_HANDLE = 9; + //public static final int STATUS_FX_NO_SUCH_PATH = 10; + //public static final int STATUS_FX_FILE_ALREADY_EXISTS = 11; + //public static final int STATUS_FX_WRITE_PROTECT = 12; + private UnsignedInteger32 id; + private UnsignedInteger32 errorCode; + private String errorMessage; + private String languageTag; + + /** + * Creates a new SshFxpStatus object. + * + * @param id + * @param errorCode + * @param errorMessage + * @param languageTag + */ + public SshFxpStatus(UnsignedInteger32 id, UnsignedInteger32 errorCode, + String errorMessage, String languageTag) { + super(SSH_FXP_STATUS); + this.id = id; + this.errorCode = errorCode; + this.errorMessage = errorMessage; + this.languageTag = languageTag; + } + + /** + * Creates a new SshFxpStatus object. + */ + public SshFxpStatus() { + super(SSH_FXP_STATUS); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getErrorCode() { + return errorCode; + } + + /** + * + * + * @return + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * + * + * @return + */ + public String getLanguageTag() { + return languageTag; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + errorCode = bar.readUINT32(); + errorMessage = bar.readString(); + languageTag = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_STATUS"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeUINT32(errorCode); + baw.writeString(errorMessage); + baw.writeString(languageTag); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpSymlink.java b/src/com/sshtools/j2ssh/sftp/SshFxpSymlink.java new file mode 100644 index 0000000000000000000000000000000000000000..8c5d9497508b81e09bf6821996b151b8afe5b02f --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpSymlink.java @@ -0,0 +1,138 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class SshFxpSymlink extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_SYMLINK = 20; + private UnsignedInteger32 id; + private String linkpath; + private String targetpath; + + /** + * Creates a new SshFxpSymlink object. + */ + public SshFxpSymlink() { + super(SSH_FXP_SYMLINK); + } + + /** + * Creates a new SshFxpSymlink object. + * + * @param id + * @param targetpath + * @param linkpath + */ + public SshFxpSymlink(UnsignedInteger32 id, String targetpath, + String linkpath) { + super(SSH_FXP_SYMLINK); + this.id = id; + this.linkpath = linkpath; + this.targetpath = targetpath; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public String getLinkPath() { + return linkpath; + } + + /** + * + * + * @return + */ + public String getTargetPath() { + return targetpath; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + linkpath = bar.readString(); + targetpath = bar.readString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_SYMLINK"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeString(linkpath); + baw.writeString(targetpath); + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpVersion.java b/src/com/sshtools/j2ssh/sftp/SshFxpVersion.java new file mode 100644 index 0000000000000000000000000000000000000000..6336fcf0c890d8fda946f003c752b6754d346178 --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpVersion.java @@ -0,0 +1,147 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SshFxpVersion extends SubsystemMessage { + /** */ + public static final int SSH_FXP_VERSION = 2; + private UnsignedInteger32 version; + private Map extended = null; + + /** + * Creates a new SshFxpVersion object. + */ + public SshFxpVersion() { + super(SSH_FXP_VERSION); + } + + /** + * Creates a new SshFxpVersion object. + * + * @param version + * @param extended + */ + public SshFxpVersion(UnsignedInteger32 version, Map extended) { + super(SSH_FXP_VERSION); + this.version = version; + this.extended = extended; + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getVersion() { + return version; + } + + /** + * + * + * @return + */ + public Map getExtended() { + return extended; + } + + /** + * + * + * @param bar + * + * @throws IOException + * @throws InvalidMessageException + */ + public void constructMessage(ByteArrayReader bar) + throws IOException, InvalidMessageException { + version = bar.readUINT32(); + extended = new HashMap(); + + String key; + String value; + + while (bar.available() > 0) { + key = bar.readString(); + value = bar.readString(); + extended.put(key, value); + } + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_INIT"; + } + + /** + * + * + * @param baw + * + * @throws IOException + * @throws InvalidMessageException + */ + public void constructByteArray(ByteArrayWriter baw) + throws IOException, InvalidMessageException { + baw.writeUINT32(version); + + if (extended != null) { + if (extended.size() > 0) { + Iterator it = extended.entrySet().iterator(); + Map.Entry entry; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + baw.writeString((String) entry.getKey()); + baw.writeString((String) entry.getValue()); + } + } + } + } +} diff --git a/src/com/sshtools/j2ssh/sftp/SshFxpWrite.java b/src/com/sshtools/j2ssh/sftp/SshFxpWrite.java new file mode 100644 index 0000000000000000000000000000000000000000..a4d3d7d2d95fd02830ae9262da0a5a3670fc5b5b --- /dev/null +++ b/src/com/sshtools/j2ssh/sftp/SshFxpWrite.java @@ -0,0 +1,156 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.sftp; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.io.UnsignedInteger32; +import com.sshtools.j2ssh.io.UnsignedInteger64; +import com.sshtools.j2ssh.subsystem.SubsystemMessage; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshFxpWrite extends SubsystemMessage implements MessageRequestId { + /** */ + public static final int SSH_FXP_WRITE = 6; + private UnsignedInteger32 id; + private byte[] handle; + private UnsignedInteger64 offset; + private byte[] data; + + /** + * Creates a new SshFxpWrite object. + */ + public SshFxpWrite() { + super(SSH_FXP_WRITE); + } + + /** + * Creates a new SshFxpWrite object. + * + * @param id + * @param handle + * @param offset + * @param data + * @param off + * @param len + */ + public SshFxpWrite(UnsignedInteger32 id, byte[] handle, + UnsignedInteger64 offset, byte[] data, int off, int len) { + super(SSH_FXP_WRITE); + this.id = id; + this.handle = handle; + this.offset = offset; + this.data = new byte[len]; + System.arraycopy(data, off, this.data, 0, len); + } + + /** + * + * + * @return + */ + public UnsignedInteger32 getId() { + return id; + } + + /** + * + * + * @return + */ + public byte[] getHandle() { + return handle; + } + + /** + * + * + * @return + */ + public UnsignedInteger64 getOffset() { + return offset; + } + + /** + * + * + * @return + */ + public byte[] getData() { + return data; + } + + /** + * + * + * @param bar + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructMessage(ByteArrayReader bar) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + id = bar.readUINT32(); + handle = bar.readBinaryString(); + offset = bar.readUINT64(); + data = bar.readBinaryString(); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_FXP_WRITE"; + } + + /** + * + * + * @param baw + * + * @throws java.io.IOException + * @throws com.sshtools.j2ssh.transport.InvalidMessageException DOCUMENT + * ME! + */ + public void constructByteArray(ByteArrayWriter baw) + throws java.io.IOException, + com.sshtools.j2ssh.transport.InvalidMessageException { + baw.writeUINT32(id); + baw.writeBinaryString(handle); + baw.writeUINT64(offset); + baw.writeBinaryString(data); + } +} diff --git a/src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java b/src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..4cedeaf2a13a0db77dee9ec672c80c06545f7e88 --- /dev/null +++ b/src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java @@ -0,0 +1,176 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.subsystem; + +import com.sshtools.j2ssh.connection.*; +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.transport.*; + +import org.apache.commons.logging.*; + +import java.io.*; + + +public abstract class SubsystemChannel extends Channel { + private static Log log = LogFactory.getLog(SubsystemChannel.class); + Integer exitCode = null; + String name; + protected SubsystemMessageStore messageStore; + DynamicBuffer buffer = new DynamicBuffer(); + int nextMessageLength = -1; + + public SubsystemChannel(String name) { + this.name = name; + this.messageStore = new SubsystemMessageStore(); + } + + public SubsystemChannel(String name, SubsystemMessageStore messageStore) { + this.name = name; + this.messageStore = messageStore; + } + + public String getChannelType() { + return "session"; + } + + protected void sendMessage(SubsystemMessage msg) + throws InvalidMessageException, IOException { + if (log.isDebugEnabled()) { + log.debug("Sending " + msg.getMessageName() + " subsystem message"); + } + + byte[] msgdata = msg.toByteArray(); + + // Write the message length + sendChannelData(ByteArrayWriter.encodeInt(msgdata.length)); + + // Write the message data + sendChannelData(msgdata); + } + + protected void onChannelRequest(String requestType, boolean wantReply, + byte[] requestData) throws java.io.IOException { + log.debug("Channel Request received: " + requestType); + + if (requestType.equals("exit-status")) { + exitCode = new Integer((int) ByteArrayReader.readInt(requestData, 0)); + log.debug("Exit code of " + exitCode.toString() + " received"); + } else if (requestType.equals("exit-signal")) { + ByteArrayReader bar = new ByteArrayReader(requestData); + String signal = bar.readString(); + boolean coredump = bar.read() != 0; + String message = bar.readString(); + String language = bar.readString(); + log.debug("Exit signal " + signal + " received"); + log.debug("Signal message: " + message); + log.debug("Core dumped: " + String.valueOf(coredump)); + + /*if (signalListener != null) { + signalListener.onExitSignal(signal, coredump, message); + }*/ + } else if (requestType.equals("xon-xoff")) { + /*if (requestData.length >= 1) { + localFlowControl = (requestData[0] != 0); + }*/ + } else if (requestType.equals("signal")) { + String signal = ByteArrayReader.readString(requestData, 0); + log.debug("Signal " + signal + " received"); + + /*if (signalListener != null) { + signalListener.onSignal(signal); + }*/ + } else { + if (wantReply) { + connection.sendChannelRequestFailure(this); + } + } + } + + protected void onChannelExtData(SshMsgChannelExtendedData msg) + throws java.io.IOException { + } + + protected void onChannelData(SshMsgChannelData msg) + throws java.io.IOException { + // Write the data to a temporary buffer that may also contain data + // that has not been processed + buffer.getOutputStream().write(msg.getChannelData()); + + int read; + byte[] tmp = new byte[4]; + byte[] msgdata; + + // Now process any outstanding messages + while (buffer.getInputStream().available() > 4) { + if (nextMessageLength == -1) { + read = 0; + + while ((read += buffer.getInputStream().read(tmp)) < 4) { + ; + } + + nextMessageLength = (int) ByteArrayReader.readInt(tmp, 0); + } + + if (buffer.getInputStream().available() >= nextMessageLength) { + msgdata = new byte[nextMessageLength]; + buffer.getInputStream().read(msgdata); + messageStore.addMessage(msgdata); + nextMessageLength = -1; + } else { + break; + } + } + } + + protected void onChannelEOF() throws java.io.IOException { + } + + protected void onChannelClose() throws java.io.IOException { + if (messageStore != null) messageStore.close(); + } + + public byte[] getChannelOpenData() { + return null; + } + + protected void onChannelOpen() throws java.io.IOException { + } + + public boolean startSubsystem() throws IOException { + log.info("Starting " + name + " subsystem"); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(name); + + return connection.sendChannelRequest(this, "subsystem", true, + baw.toByteArray()); + } + + public byte[] getChannelConfirmationData() { + return null; + } +} diff --git a/src/com/sshtools/j2ssh/subsystem/SubsystemClient.java b/src/com/sshtools/j2ssh/subsystem/SubsystemClient.java new file mode 100644 index 0000000000000000000000000000000000000000..8417e72b08d3526b707a6b021b8490fc7738833a --- /dev/null +++ b/src/com/sshtools/j2ssh/subsystem/SubsystemClient.java @@ -0,0 +1,239 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.subsystem; + +import com.sshtools.j2ssh.SshThread; +import com.sshtools.j2ssh.connection.ChannelState; +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.session.SessionChannelClient; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.util.StartStopState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.33 $ + */ +public abstract class SubsystemClient implements Runnable { + private static Log log = LogFactory.getLog(SubsystemClient.class); + private InputStream in; + private OutputStream out; + private Thread thread; + private String name; + private StartStopState state = new StartStopState(StartStopState.STOPPED); + + /** */ + protected SubsystemMessageStore messageStore; + + /** */ + protected SessionChannelClient session; + + /** + * Creates a new SubsystemClient object. + * + * @param name + */ + public SubsystemClient(String name) { + this.name = name; + messageStore = new SubsystemMessageStore(); + } + + /** + * Creates a new SubsystemClient object. + * + * @param name + * @param messageStore + */ + public SubsystemClient(String name, SubsystemMessageStore messageStore) { + this.name = name; + this.messageStore = messageStore; + } + + /** + * + * + * @return + */ + public boolean isClosed() { + return state.getValue() == StartStopState.STOPPED; + } + + /** + * + * + * @param session + */ + public void setSessionChannel(SessionChannelClient session) { + this.session = session; + this.in = session.getInputStream(); + this.out = session.getOutputStream(); + session.setName(name); + } + + /** + * + * + * @return + */ + public SessionChannelClient getSessionChannel() { + return this.session; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public boolean start() throws IOException { + thread = new SshThread(this, name + " subsystem", true); + + if (session == null) { + throw new IOException( + "No valid session is attached to the subsystem!"); + } + + if (session.getState().getValue() != ChannelState.CHANNEL_OPEN) { + throw new IOException("The session is not open!"); + } + + thread.start(); + + return onStart(); + } + + /** + * + * + * @return + * + * @throws IOException + */ + protected abstract boolean onStart() throws IOException; + + /** + * + * + * @return + */ + public String getName() { + return name; + } + + /** + * + * + * @param msg + * + * @throws InvalidMessageException + * @throws IOException + */ + protected void sendMessage(SubsystemMessage msg) + throws InvalidMessageException, IOException { + if (log.isDebugEnabled()) { + log.debug("Sending " + msg.getMessageName() + " subsystem message"); + } + + byte[] msgdata = msg.toByteArray(); + + // Write the message length + out.write(ByteArrayWriter.encodeInt(msgdata.length)); + + // Write the message data + out.write(msgdata); + } + + /** + * + */ + public void run() { + int read; + int len; + int pos; + byte[] buffer = new byte[4]; + byte[] msg; + state.setValue(StartStopState.STARTED); + + try { + // read the first four bytes of data to determine the susbsytem + // message length + while ((state.getValue() == StartStopState.STARTED) && + (session.getState().getValue() == ChannelState.CHANNEL_OPEN)) { + read = in.read(buffer); + + if (read > 0) { + len = (int) ByteArrayReader.readInt(buffer, 0); + msg = new byte[len]; + pos = 0; + + while (pos < len) { + read = in.read(msg, pos, msg.length - pos); + + if (read > 0) { + pos += read; + } else if (read == -1) { + break; + } + } + + messageStore.addMessage(msg); + msg = null; + } else if (read == -1) { + break; + } + } + } catch (IOException ioe) { + log.fatal("Subsystem message loop failed!", ioe); + } finally { + state.setValue(StartStopState.STOPPED); + } + + thread = null; + } + + /** + * + * + * @throws IOException + */ + public void stop() throws IOException { + state.setValue(StartStopState.STOPPED); + in.close(); + out.close(); + session.close(); + } +} diff --git a/src/com/sshtools/j2ssh/subsystem/SubsystemInputStream.java b/src/com/sshtools/j2ssh/subsystem/SubsystemInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..83a604202f1f58556b5337012bae7487af0bb1e3 --- /dev/null +++ b/src/com/sshtools/j2ssh/subsystem/SubsystemInputStream.java @@ -0,0 +1,103 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.subsystem; + +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; +import java.io.InputStream; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class SubsystemInputStream extends InputStream { + byte[] msgdata; + int currentPos = 0; + private SubsystemMessageStore messageStore; + + /** + * Creates a new SubsystemInputStream object. + * + * @param messageStore + */ + public SubsystemInputStream(SubsystemMessageStore messageStore) { + this.messageStore = messageStore; + } + + /** + * + * + * @return + */ + public int available() { + if (msgdata == null) { + return 0; + } + + return msgdata.length - currentPos; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public int read() throws IOException { + if (msgdata == null) { + collectNextMessage(); + } + + if (currentPos >= msgdata.length) { + collectNextMessage(); + } + + return msgdata[currentPos++] & 0xFF; + } + + private void collectNextMessage() throws IOException { + SubsystemMessage msg = messageStore.nextMessage(); + + try { + ByteArrayWriter baw = new ByteArrayWriter(); + byte[] data = msg.toByteArray(); + baw.writeInt(data.length); + baw.write(data); + msgdata = baw.toByteArray(); + } catch (InvalidMessageException ime) { + throw new IOException( + "An invalid message was encountered in the inputstream"); + } + + currentPos = 0; + } +} diff --git a/src/com/sshtools/j2ssh/subsystem/SubsystemMessage.java b/src/com/sshtools/j2ssh/subsystem/SubsystemMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..bbf58d4a350d6c5b98672e418b003ca4d4b54f78 --- /dev/null +++ b/src/com/sshtools/j2ssh/subsystem/SubsystemMessage.java @@ -0,0 +1,134 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.subsystem; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public abstract class SubsystemMessage { + private int type; + + /** + * Creates a new SubsystemMessage object. + * + * @param type + */ + public SubsystemMessage(int type) { + this.type = type; + } + + /** + * + * + * @return + */ + public abstract String getMessageName(); + + /** + * + * + * @return + */ + public int getMessageType() { + return type; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + * @throws IOException + */ + public abstract void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException, IOException; + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + * @throws IOException + */ + public abstract void constructMessage(ByteArrayReader bar) + throws InvalidMessageException, IOException; + + /** + * + * + * @param data + * + * @throws InvalidMessageException + */ + public void fromByteArray(byte[] data) throws InvalidMessageException { + try { + ByteArrayReader bar = new ByteArrayReader(data); + + if (bar.available() > 0) { + type = bar.read(); + constructMessage(bar); + } else { + throw new InvalidMessageException( + "Not enough message data to complete the message"); + } + } catch (IOException ioe) { + throw new InvalidMessageException( + "The message data cannot be read!"); + } + } + + /** + * + * + * @return + * + * @throws InvalidMessageException + */ + public byte[] toByteArray() throws InvalidMessageException { + try { + ByteArrayWriter baw = new ByteArrayWriter(); + baw.write(type); + constructByteArray(baw); + + return baw.toByteArray(); + } catch (IOException ioe) { + throw new InvalidMessageException( + "The message data cannot be written!"); + } + } +} diff --git a/src/com/sshtools/j2ssh/subsystem/SubsystemMessageStore.java b/src/com/sshtools/j2ssh/subsystem/SubsystemMessageStore.java new file mode 100644 index 0000000000000000000000000000000000000000..ac4ef30e184a4180912309ddbd0d314ecef9d5e6 --- /dev/null +++ b/src/com/sshtools/j2ssh/subsystem/SubsystemMessageStore.java @@ -0,0 +1,194 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.subsystem; + +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.MessageNotAvailableException; +import com.sshtools.j2ssh.transport.MessageStoreEOFException; +import com.sshtools.j2ssh.util.OpenClosedState; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.28 $ + */ +public class SubsystemMessageStore { + private static Log log = LogFactory.getLog(SubsystemMessageStore.class); + + // List to hold messages as they are received + + /** */ + protected List messages = new ArrayList(); + + // Map to hold message implementation classes + + /** */ + protected Map registeredMessages = new HashMap(); + private OpenClosedState state = new OpenClosedState(OpenClosedState.OPEN); + + /** + * Creates a new SubsystemMessageStore object. + */ + public SubsystemMessageStore() { + } + + /** + * + * + * @param msg + */ + public synchronized void addMessage(SubsystemMessage msg) { + if (log.isDebugEnabled()) { + log.debug("Received " + msg.getMessageName() + + " subsystem message"); + } + + // Add the message + messages.add(msg); + + // Notify the threads + notifyAll(); + } + + /** + * + * + * @param msgdata + * + * @throws InvalidMessageException + */ + public synchronized void addMessage(byte[] msgdata) + throws InvalidMessageException { + try { + Class impl = (Class) registeredMessages.get(new Integer(msgdata[0])); + + if (impl == null) { + throw new InvalidMessageException("The message with id " + + String.valueOf(msgdata[0]) + " is not implemented"); + } + + SubsystemMessage msg = (SubsystemMessage) impl.newInstance(); + msg.fromByteArray(msgdata); + addMessage(msg); + + return; + } catch (IllegalAccessException iae) { + } catch (InstantiationException ie) { + } + + throw new InvalidMessageException("Could not instantiate message class"); + } + + /** + * + * + * @return + * + * @throws MessageStoreEOFException + */ + public synchronized SubsystemMessage nextMessage() + throws MessageStoreEOFException { + try { + return nextMessage(0); + } catch (MessageNotAvailableException mnae) { + return null; + } + } + + /** + * + * + * @param timeout + * + * @return + * + * @throws MessageStoreEOFException + * @throws MessageNotAvailableException + */ + public synchronized SubsystemMessage nextMessage(int timeout) + throws MessageStoreEOFException, MessageNotAvailableException { + // If there are no messages available then wait untill there are. + timeout = (timeout > 0) ? timeout : 0; + + while (messages.size() <= 0) { + try { + wait(timeout); + + if (timeout > 0) { + break; + } + } catch (InterruptedException e) { + } + } + + if (state.getValue() != OpenClosedState.OPEN) { + throw new MessageStoreEOFException(); + } + + if (messages.size() > 0) { + return (SubsystemMessage) messages.remove(0); + } else { + throw new MessageNotAvailableException(); + } + } + + /** + * + * + * @param messageId + * @param implementor + */ + public void registerMessage(int messageId, Class implementor) { + registeredMessages.put(new Integer(messageId), implementor); + } + + /** + * + * + * @return + */ + public OpenClosedState getState() { + return state; + } + + /** + * + */ + public synchronized void close() { + state.setValue(OpenClosedState.CLOSED); + notifyAll(); + } +} diff --git a/src/com/sshtools/j2ssh/subsystem/SubsystemOutputStream.java b/src/com/sshtools/j2ssh/subsystem/SubsystemOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..8c5d73d8f24fe98d0ae8335273513850e46e0ef3 --- /dev/null +++ b/src/com/sshtools/j2ssh/subsystem/SubsystemOutputStream.java @@ -0,0 +1,114 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.subsystem; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.transport.InvalidMessageException; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class SubsystemOutputStream extends OutputStream { + // Temporary storage buffer to build up a message + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + SubsystemMessageStore messageStore; + int messageStart = 0; + + /** + * Creates a new SubsystemOutputStream object. + * + * @param messageStore + */ + public SubsystemOutputStream(SubsystemMessageStore messageStore) { + super(); + this.messageStore = messageStore; + } + + /** + * + * + * @param b + * @param off + * @param len + * + * @throws IOException + */ + public void write(byte[] b, int off, int len) throws IOException { + // Write the data + super.write(b, off, len); + processMessage(); + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void write(int b) throws IOException { + buffer.write(b); + } + + private void processMessage() throws IOException { + // Now try to process a message + if (buffer.size() > (messageStart + 4)) { + int messageLength = (int) ByteArrayReader.readInt(buffer.toByteArray(), + messageStart); + + if (messageLength <= (buffer.size() - 4)) { + byte[] msgdata = new byte[messageLength]; + + // Process a message + System.arraycopy(buffer.toByteArray(), messageStart + 4, + msgdata, 0, messageLength); + + try { + messageStore.addMessage(msgdata); + } catch (InvalidMessageException ime) { + throw new IOException( + "An invalid message was encountered in the outputstream: " + + ime.getMessage()); + } + + if (messageLength == (buffer.size() - 4)) { + buffer.reset(); + messageStart = 0; + } else { + messageStart = messageLength + 4; + } + } + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/AbstractKnownHostsKeyVerification.java b/src/com/sshtools/j2ssh/transport/AbstractKnownHostsKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..67e726e5667060a33c7dcf7245b2be6e78ddc75b --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/AbstractKnownHostsKeyVerification.java @@ -0,0 +1,473 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; +import com.sshtools.j2ssh.util.Base64; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import java.security.AccessControlException; +import java.security.AccessController; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; + + +/** + * <p> + * An abstract <code>HostKeyVerification</code> class providing validation + * against the known_hosts format. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.18 $ + * + * @since 0.2.0 + */ +public abstract class AbstractKnownHostsKeyVerification + implements HostKeyVerification { + private static String defaultHostFile; + private static Log log = LogFactory.getLog(HostKeyVerification.class); + + //private List deniedHosts = new ArrayList(); + private Map allowedHosts = new HashMap(); + private String knownhosts; + private boolean hostFileWriteable; + + //private boolean expectEndElement = false; + //private String currentElement = null; + + /** + * <p> + * Constructs a host key verification instance reading the specified + * known_hosts file. + * </p> + * + * @param knownhosts the path of the known_hosts file + * + * @throws InvalidHostFileException if the known_hosts file is invalid + * + * @since 0.2.0 + */ + public AbstractKnownHostsKeyVerification(String knownhosts) + throws InvalidHostFileException { + InputStream in = null; + + try { + // If no host file is supplied, or there is not enough permission to load + // the file, then just create an empty list. + if (knownhosts != null) { + if (System.getSecurityManager() != null) { + AccessController.checkPermission(new FilePermission( + knownhosts, "read")); + } + + // Load the hosts file. Do not worry if fle doesnt exist, just disable + // save of + File f = new File(knownhosts); + + if (f.exists()) { + in = new FileInputStream(f); + + BufferedReader reader = new BufferedReader(new InputStreamReader( + in)); + String line; + + while ((line = reader.readLine()) != null) { + StringTokenizer tokens = new StringTokenizer(line, " "); + String host = (String) tokens.nextElement(); + String algorithm = (String) tokens.nextElement(); + String key = (String) tokens.nextElement(); + + SshPublicKey pk = SshKeyPairFactory.decodePublicKey(Base64.decode( + key)); + /*if (host.indexOf(",") > -1) { + host = host.substring(0, host.indexOf(",")); + }*/ + putAllowedKey(host, pk); + + //allowedHosts.put(host + "#" + pk.getAlgorithmName(), pk); + } + + reader.close(); + hostFileWriteable = f.canWrite(); + } else { + // Try to create the file and its parents if necersary + f.getParentFile().mkdirs(); + + if (f.createNewFile()) { + FileOutputStream out = new FileOutputStream(f); + out.write(toString().getBytes()); + out.close(); + hostFileWriteable = true; + } else { + hostFileWriteable = false; + } + } + + if (!hostFileWriteable) { + log.warn("Host file is not writeable."); + } + + this.knownhosts = knownhosts; + } + } catch (AccessControlException ace) { + hostFileWriteable = false; + log.warn( + "Not enough permission to load a hosts file, so just creating an empty list"); + } catch (IOException ioe) { + hostFileWriteable = false; + log.info("Could not open or read " + knownhosts + ": " + + ioe.getMessage()); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ioe) { + } + } + } + } + + /** + * <p> + * Determines whether the host file is writable. + * </p> + * + * @return true if the host file is writable, otherwise false + * + * @since 0.2.0 + */ + public boolean isHostFileWriteable() { + return hostFileWriteable; + } + + /** + * <p> + * Called by the <code>verifyHost</code> method when the host key supplied + * by the host does not match the current key recording in the known hosts + * file. + * </p> + * + * @param host the name of the host + * @param allowedHostKey the current key recorded in the known_hosts file. + * @param actualHostKey the actual key supplied by the user + * + * @throws TransportProtocolException if an error occurs + * + * @since 0.2.0 + */ + public abstract void onHostKeyMismatch(String host, + SshPublicKey allowedHostKey, SshPublicKey actualHostKey) + throws TransportProtocolException; + + /** + * <p> + * Called by the <code>verifyHost</code> method when the host key supplied + * is not recorded in the known_hosts file. + * </p> + * + * <p></p> + * + * @param host the name of the host + * @param key the public key supplied by the host + * + * @throws TransportProtocolException if an error occurs + * + * @since 0.2.0 + */ + public abstract void onUnknownHost(String host, SshPublicKey key) + throws TransportProtocolException; + + /** + * <p> + * Allows a host key, optionally recording the key to the known_hosts file. + * </p> + * + * @param host the name of the host + * @param pk the public key to allow + * @param always true if the key should be written to the known_hosts file + * + * @throws InvalidHostFileException if the host file cannot be written + * + * @since 0.2.0 + */ + public void allowHost(String host, SshPublicKey pk, boolean always) + throws InvalidHostFileException { + if (log.isDebugEnabled()) { + log.debug("Allowing " + host + " with fingerprint " + + pk.getFingerprint()); + } + + // Put the host into the allowed hosts list, overiding any previous + // entry + putAllowedKey(host, pk); + + //allowedHosts.put(host, pk); + // If we always want to allow then save the host file with the + // new details + if (always) { + saveHostFile(); + } + } + + /** + * <p> + * Returns a Map of the allowed hosts. + * </p> + * + * <p> + * The keys of the returned Map are comma separated strings of + * "hostname,ipaddress". The value objects are Maps containing a string + * key of the public key alogorithm name and the public key as the value. + * </p> + * + * @return the allowed hosts + * + * @since 0.2.0 + */ + public Map allowedHosts() { + return allowedHosts; + } + + /** + * <p> + * Removes an allowed host. + * </p> + * + * @param host the host to remove + * + * @since 0.2.0 + */ + public void removeAllowedHost(String host) { + Iterator it = allowedHosts.keySet().iterator(); + + while (it.hasNext()) { + StringTokenizer tokens = new StringTokenizer((String) it.next(), ","); + + while (tokens.hasMoreElements()) { + String name = (String) tokens.nextElement(); + + if (name.equals(host)) { + allowedHosts.remove(name); + } + } + } + } + + /** + * <p> + * Verifies a host key against the list of known_hosts. + * </p> + * + * <p> + * If the host unknown or the key does not match the currently allowed host + * key the abstract <code>onUnknownHost</code> or + * <code>onHostKeyMismatch</code> methods are called so that the caller + * may identify and allow the host. + * </p> + * + * @param host the name of the host + * @param pk the host key supplied + * + * @return true if the host is accepted, otherwise false + * + * @throws TransportProtocolException if an error occurs + * + * @since 0.2.0 + */ + public boolean verifyHost(String host, SshPublicKey pk) + throws TransportProtocolException { + String fingerprint = pk.getFingerprint(); + log.info("Verifying " + host + " host key"); + + if (log.isDebugEnabled()) { + log.debug("Fingerprint: " + fingerprint); + } + + Iterator it = allowedHosts.keySet().iterator(); + + while (it.hasNext()) { + // Could be a comma delimited string of names/ip addresses + String names = (String) it.next(); + + if (names.equals(host)) { + return validateHost(names, pk); + } + + StringTokenizer tokens = new StringTokenizer(names, ","); + + while (tokens.hasMoreElements()) { + // Try the allowed hosts by looking at the allowed hosts map + String name = (String) tokens.nextElement(); + + if (name.equalsIgnoreCase(host)) { + return validateHost(names, pk); + } + } + } + + // The host is unknown os ask the user + onUnknownHost(host, pk); + + // Recheck ans return the result + return checkKey(host, pk); + } + + private boolean validateHost(String names, SshPublicKey pk) + throws TransportProtocolException { + // The host is allowed so check the fingerprint + SshPublicKey pub = getAllowedKey(names, pk.getAlgorithmName()); //shPublicKey) allowedHosts.get(host + "#" + pk.getAlgorithmName()); + + if ((pub != null) && pk.equals(pub)) { + return true; + } else { + // The host key does not match the recorded so call the abstract + // method so that the user can decide + if (pub == null) { + onUnknownHost(names, pk); + } else { + onHostKeyMismatch(names, pub, pk); + } + + // Recheck the after the users input + return checkKey(names, pk); + } + } + + private boolean checkKey(String host, SshPublicKey key) { + SshPublicKey pk = getAllowedKey(host, key.getAlgorithmName()); //shPublicKey) allowedHosts.get(host + "#" + key.getAlgorithmName()); + + if (pk != null) { + if (pk.equals(key)) { + return true; + } + } + + return false; + } + + private SshPublicKey getAllowedKey(String names, String algorithm) { + if (allowedHosts.containsKey(names)) { + Map map = (Map) allowedHosts.get(names); + + return (SshPublicKey) map.get(algorithm); + } + + return null; + } + + private void putAllowedKey(String host, SshPublicKey key) { + if (!allowedHosts.containsKey(host)) { + allowedHosts.put(host, new HashMap()); + } + + Map map = (Map) allowedHosts.get(host); + map.put(key.getAlgorithmName(), key); + } + + /** + * <p> + * Save's the host key file to be saved. + * </p> + * + * @throws InvalidHostFileException if the host file is invalid + * + * @since 0.2.0 + */ + public void saveHostFile() throws InvalidHostFileException { + if (!hostFileWriteable) { + throw new InvalidHostFileException("Host file is not writeable."); + } + + log.info("Saving " + defaultHostFile); + + try { + File f = new File(knownhosts); + FileOutputStream out = new FileOutputStream(f); + out.write(toString().getBytes()); + out.close(); + } catch (IOException e) { + throw new InvalidHostFileException("Could not write to " + + knownhosts); + } + } + + /** + * <p> + * Outputs the allowed hosts in the known_hosts file format. + * </p> + * + * <p> + * The format consists of any number of lines each representing one key for + * a single host. + * </p> + * <code> titan,192.168.1.12 ssh-dss AAAAB3NzaC1kc3MAAACBAP1/U4Ed..... + * titan,192.168.1.12 ssh-rsa AAAAB3NzaC1kc3MAAACBAP1/U4Ed..... + * einstein,192.168.1.40 ssh-dss AAAAB3NzaC1kc3MAAACBAP1/U4Ed..... </code> + * + * @return + * + * @since 0.2.0 + */ + public String toString() { + String knownhosts = ""; + Map.Entry entry; + Map.Entry entry2; + Iterator it = allowedHosts.entrySet().iterator(); + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + + Iterator it2 = ((Map) entry.getValue()).entrySet().iterator(); + + while (it2.hasNext()) { + entry2 = (Map.Entry) it2.next(); + + SshPublicKey pk = (SshPublicKey) entry2.getValue(); + knownhosts += (entry.getKey().toString() + " " + + pk.getAlgorithmName() + " " + + Base64.encodeBytes(pk.getEncoded(), true) + "\n"); + } + } + + return knownhosts; + } +} diff --git a/src/com/sshtools/j2ssh/transport/AlgorithmInitializationException.java b/src/com/sshtools/j2ssh/transport/AlgorithmInitializationException.java new file mode 100644 index 0000000000000000000000000000000000000000..02a08646b3976886dc8ea90acbe41fa25de1d007 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/AlgorithmInitializationException.java @@ -0,0 +1,53 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by the transport protocol if an error occurs during any type of + * algorithm initialization. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.18 $ + * + * @since 0.2.0 + */ +public class AlgorithmInitializationException extends TransportProtocolException { + /** + * <p> + * Constructs the exception. + * </p> + * + * @param msg the error message + * + * @since 0.2.0 + */ + public AlgorithmInitializationException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/AlgorithmNotAgreedException.java b/src/com/sshtools/j2ssh/transport/AlgorithmNotAgreedException.java new file mode 100644 index 0000000000000000000000000000000000000000..37483eb5bc636df9c7167a400f18ce7943f795ff --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/AlgorithmNotAgreedException.java @@ -0,0 +1,55 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by the transport protocol if an algortihm cannot be agreed between + * the client and server. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.19 $ + * + * @since 0.2.0 + */ +public class AlgorithmNotAgreedException extends TransportProtocolException { + /** + * <p> + * Constructs the exception. + * </p> + * + * <p></p> + * + * @param msg the error message + * + * @since 0.2.0 + */ + public AlgorithmNotAgreedException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/AlgorithmNotSupportedException.java b/src/com/sshtools/j2ssh/transport/AlgorithmNotSupportedException.java new file mode 100644 index 0000000000000000000000000000000000000000..a74ad7cda3515cdcc2961ae29e66bd627f7f3f3f --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/AlgorithmNotSupportedException.java @@ -0,0 +1,53 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by the transport protocol if an algorithm is not supported by the + * underlying JCE. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.19 $ + * + * @since 0.2.0 + */ +public class AlgorithmNotSupportedException extends TransportProtocolException { + /** + * <p> + * Constructs the exception. + * </p> + * + * @param msg the error message + * + * @since 0.2.0 + */ + public AlgorithmNotSupportedException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/AlgorithmOperationException.java b/src/com/sshtools/j2ssh/transport/AlgorithmOperationException.java new file mode 100644 index 0000000000000000000000000000000000000000..0ff1f6170cde472b4de98c9a5671677e2d71727e --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/AlgorithmOperationException.java @@ -0,0 +1,52 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by the transport protocol if an algorithm operation error occurs. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.19 $ + * + * @since 0.2.0 + */ +public class AlgorithmOperationException extends TransportProtocolException { + /** + * <p> + * Contructs the exception. + * </p> + * + * @param msg the error message + * + * @since 0.2.0 + */ + public AlgorithmOperationException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/AsyncService.java b/src/com/sshtools/j2ssh/transport/AsyncService.java new file mode 100644 index 0000000000000000000000000000000000000000..48d2028202b608435e8e2858893be1c8c7c590c6 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/AsyncService.java @@ -0,0 +1,170 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.SshThread; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + + +/** + * <p> + * Extends the simple <code>Service</code> class to provide an asyncronous + * messaging service for the transport protocol. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.28 $ + * + * @since 0.2.0 + */ +public abstract class AsyncService extends Service implements Runnable { + private static Log log = LogFactory.getLog(Service.class); + + /** */ + protected SshThread thread; + + /** + * <p> + * Constructs an asyncronous service. + * </p> + * + * @param serviceName the name of the service + * + * @since 0.2.0 + */ + public AsyncService(String serviceName) { + super(serviceName); + } + + /** + * <p> + * Implements the abstract <code>Service</code> method and starts the + * service thread. + * </p> + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + protected void onStart() throws IOException { + if (Thread.currentThread() instanceof SshThread) { + thread = ((SshThread) Thread.currentThread()).cloneThread(this, + getServiceName()); + } else { + thread = new SshThread(this, getServiceName(), true); + } + + log.info("Starting " + getServiceName() + " service thread"); + thread.start(); + } + + /** + * <p> + * Implements the asyncronous services message loop. + * </p> + * + * @since 0.2.0 + */ + public final void run() { + int[] messageFilter = getAsyncMessageFilter(); + state.setValue(ServiceState.SERVICE_STARTED); + + SshMessage msg = null; + + while ((state.getValue() == ServiceState.SERVICE_STARTED) && + transport.isConnected()) { + try { + // Get the next message from the message store + msg = messageStore.getMessage(messageFilter); + + if (state.getValue() == ServiceState.SERVICE_STOPPED) { + break; + } + + if (log.isDebugEnabled()) { + log.debug("Routing " + msg.getMessageName()); + } + + onMessageReceived(msg); + + if (log.isDebugEnabled()) { + log.debug("Finished processing " + msg.getMessageName()); + } + } catch (MessageStoreEOFException eof) { + stop(); + } catch (Exception ex) { + if ((state.getValue() != ServiceState.SERVICE_STOPPED) && + transport.isConnected()) { + log.fatal("Service message loop failed!", ex); + stop(); + } + } + } + + onStop(); + log.info(getServiceName() + " thread is exiting"); + thread = null; + } + + /** + * <p> + * The service thread calls this method when the thread is exiting. + * </p> + * + * @since 0.2.0 + */ + protected abstract void onStop(); + + /** + * <p> + * Implement this method by returning the message ids of the asyncrounous + * messages your implementation wants to receive. + * </p> + * + * @return an int array of message ids + * + * @since 0.2.0 + */ + protected abstract int[] getAsyncMessageFilter(); + + /** + * <p> + * Called by the service thread when an asyncronous message is received. + * </p> + * + * @param msg the message received + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + protected abstract void onMessageReceived(SshMessage msg) + throws IOException; +} diff --git a/src/com/sshtools/j2ssh/transport/ConsoleKnownHostsKeyVerification.java b/src/com/sshtools/j2ssh/transport/ConsoleKnownHostsKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..85b6b2c7ed86e4d9e7d0d1b3ba758f141a535ea9 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/ConsoleKnownHostsKeyVerification.java @@ -0,0 +1,161 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + + +/** + * <p> + * Implements the <code>AbstractKnownHostsKeyVerification</code> to provide + * host key verification through the console. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.14 $ + * + * @since 0.2.0 + */ +public class ConsoleKnownHostsKeyVerification + extends AbstractKnownHostsKeyVerification { + /** + * <p> + * Constructs the verification instance with the default known_hosts file + * from $HOME/.ssh/known_hosts. + * </p> + * + * @throws InvalidHostFileException if the known_hosts file is invalid. + * + * @since 0.2.0 + */ + public ConsoleKnownHostsKeyVerification() throws InvalidHostFileException { + super(new File(System.getProperty("user.home"), + ".ssh" + File.separator + "known_hosts").getAbsolutePath()); + } + + /** + * <p> + * Constructs the verification instance with the specified known_hosts + * file. + * </p> + * + * @param knownhosts the path to the known_hosts file + * + * @throws InvalidHostFileException if the known_hosts file is invalid. + * + * @since 0.2.0 + */ + public ConsoleKnownHostsKeyVerification(String knownhosts) + throws InvalidHostFileException { + super(knownhosts); + } + + /** + * <p> + * Prompts the user through the console to verify the host key. + * </p> + * + * @param host the name of the host + * @param pk the current public key of the host + * @param actual the actual public key supplied by the host + * + * @since 0.2.0 + */ + public void onHostKeyMismatch(String host, SshPublicKey pk, + SshPublicKey actual) { + try { + System.out.println("The host key supplied by " + host + " is: " + + actual.getFingerprint()); + System.out.println("The current allowed key for " + host + " is: " + + pk.getFingerprint()); + getResponse(host, pk); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * <p> + * Prompts the user through the console to verify the host key. + * </p> + * + * @param host the name of the host + * @param pk the public key supplied by the host + * + * @since 0.2.0 + */ + public void onUnknownHost(String host, SshPublicKey pk) { + try { + System.out.println("The host " + host + + " is currently unknown to the system"); + System.out.println("The host key fingerprint is: " + + pk.getFingerprint()); + getResponse(host, pk); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void getResponse(String host, SshPublicKey pk) + throws InvalidHostFileException, IOException { + String response = ""; + BufferedReader reader = new BufferedReader(new InputStreamReader( + System.in)); + + while (!(response.equalsIgnoreCase("YES") || + response.equalsIgnoreCase("NO") || + (response.equalsIgnoreCase("ALWAYS") && isHostFileWriteable()))) { + String options = (isHostFileWriteable() ? "Yes|No|Always" : "Yes|No"); + + if (!isHostFileWriteable()) { + System.out.println( + "Always option disabled, host file is not writeable"); + } + + System.out.print("Do you want to allow this host key? [" + options + + "]: "); + response = reader.readLine(); + } + + if (response.equalsIgnoreCase("YES")) { + allowHost(host, pk, false); + } + + if (response.equalsIgnoreCase("NO")) { + System.out.println("Cannot continue without a valid host key"); + System.exit(1); + } + + if (response.equalsIgnoreCase("ALWAYS") && isHostFileWriteable()) { + allowHost(host, pk, true); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/HostKeyVerification.java b/src/com/sshtools/j2ssh/transport/HostKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..c0be72f32c2a698623621a5a4409d1bee48b8ba4 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/HostKeyVerification.java @@ -0,0 +1,60 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + + +/** + * <p> + * An interface to allow the transport protocol to verify the public key + * supplied by the server during key-exchange + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.29 $ + * + * @since 0.2.0 + */ +public interface HostKeyVerification { + /** + * <p> + * Called by the transport protocol to verify the identity of the server + * through the supplied public key. + * </p> + * + * @param host the name of the host + * @param pk the public key supplied during key-exchange + * + * @return true if the host is acceptable, otherwise false + * + * @throws TransportProtocolException if an error occurs + * + * @since 0.2.0 + */ + public boolean verifyHost(String host, SshPublicKey pk) + throws TransportProtocolException; +} diff --git a/src/com/sshtools/j2ssh/transport/IgnoreHostKeyVerification.java b/src/com/sshtools/j2ssh/transport/IgnoreHostKeyVerification.java new file mode 100644 index 0000000000000000000000000000000000000000..a2b6dbc39160dbbaf7cc5e3f8316f052fa0d8394 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/IgnoreHostKeyVerification.java @@ -0,0 +1,62 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + + +/** + * <p> + * A simple host key verification implementation that automatically approves + * the servers host key. It should be noted that using this implementation + * will render the protocol insecure against active attacks. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.15 $ + * + * @since 0.2.0 + */ +public class IgnoreHostKeyVerification implements HostKeyVerification { + /** + * <p> + * Simply returns <code>true</code> to all requests. + * </p> + * + * @param host the name of the host + * @param pk the hosts public key + * + * @return <code>true</code> + * + * @throws TransportProtocolException if an error occurs + * + * @since 0.2.0 + */ + public boolean verifyHost(String host, SshPublicKey pk) + throws TransportProtocolException { + return true; + } +} diff --git a/src/com/sshtools/j2ssh/transport/InvalidHostFileException.java b/src/com/sshtools/j2ssh/transport/InvalidHostFileException.java new file mode 100644 index 0000000000000000000000000000000000000000..f38ca1ce2842d65433a257332f13a05d220569f1 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/InvalidHostFileException.java @@ -0,0 +1,52 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by abstract host key verifications when a host file is invalid + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.20 $ + * + * @since 0.2.0 + */ +public class InvalidHostFileException extends TransportProtocolException { + /** + * <p> + * Contructs the exception. + * </p> + * + * @param msg the error message + * + * @since 0.2.0 + */ + public InvalidHostFileException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/InvalidMessageException.java b/src/com/sshtools/j2ssh/transport/InvalidMessageException.java new file mode 100644 index 0000000000000000000000000000000000000000..b524d67457c0dbb70c341b8aece5eed5bfc586df --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/InvalidMessageException.java @@ -0,0 +1,53 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by <code>SshMessage</code> implementations when an invalid message is + * found. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.19 $ + * + * @since 0.2.0 + */ +public class InvalidMessageException extends TransportProtocolException { + /** + * <p> + * Constructs the message. + * </p> + * + * @param msg the error description + * + * @since 0.2.0 + */ + public InvalidMessageException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/MessageAlreadyRegisteredException.java b/src/com/sshtools/j2ssh/transport/MessageAlreadyRegisteredException.java new file mode 100644 index 0000000000000000000000000000000000000000..ac166d54e464a59c904b1f08259c1524d86a1942 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/MessageAlreadyRegisteredException.java @@ -0,0 +1,54 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.*; + + +/** + * <p> + * Thrown by message store when a message is already registered + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.19 $ + * + * @since 0.2.0 + */ +public class MessageAlreadyRegisteredException extends SshException { + /** + * <p> + * Constructs the exception. + * </p> + * + * @param messageId the id of the message already registered + * + * @since 0.2.0 + */ + public MessageAlreadyRegisteredException(Integer messageId) { + super("Message Id " + messageId.toString() + " is already registered"); + } +} diff --git a/src/com/sshtools/j2ssh/transport/MessageNotAvailableException.java b/src/com/sshtools/j2ssh/transport/MessageNotAvailableException.java new file mode 100644 index 0000000000000000000000000000000000000000..9dccce4962c174c486af1ebd065ce6f734984d83 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/MessageNotAvailableException.java @@ -0,0 +1,48 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by the message store when a message is not available. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.14 $ + * + * @since 0.2.0 + */ +public class MessageNotAvailableException extends Exception { + /** + * <p> + * Constructs the excpetion. + * </p> + */ + public MessageNotAvailableException() { + super("The message is not available"); + } +} diff --git a/src/com/sshtools/j2ssh/transport/MessageNotRegisteredException.java b/src/com/sshtools/j2ssh/transport/MessageNotRegisteredException.java new file mode 100644 index 0000000000000000000000000000000000000000..a0b7704dc7a0e1af793f8bf3e04763c5f882e6d9 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/MessageNotRegisteredException.java @@ -0,0 +1,71 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.SshException; + + +/** + * <p> + * Thrown by the message store when a message is added which is not registered. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.20 $ + * + * @since 0.2.0 + */ +public class MessageNotRegisteredException extends SshException { + /** + * <p> + * Constructs the exception. + * </p> + * + * @param messageId the id of the message not registered + * + * @since 0.2.0 + */ + public MessageNotRegisteredException(Integer messageId) { + super("Message Id " + messageId.toString() + + " is not currently registered"); + } + + /** + * <p> + * Consructs the exception. + * </p> + * + * @param messageId the id of the message not registered + * @param store the message store + * + * @since 0.2.0 + */ + public MessageNotRegisteredException(Integer messageId, + SshMessageStore store) { + super("Message Id " + messageId.toString() + + " is not registered to the message store specified"); + } +} diff --git a/src/com/sshtools/j2ssh/transport/MessageStoreEOFException.java b/src/com/sshtools/j2ssh/transport/MessageStoreEOFException.java new file mode 100644 index 0000000000000000000000000000000000000000..cdf2a43b4492bcbfd518b6eb3a6a878feb27eda1 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/MessageStoreEOFException.java @@ -0,0 +1,48 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Thrown by the message store when the store reaches EOF. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.15 $ + * + * @since 0.2.0 + */ +public class MessageStoreEOFException extends TransportProtocolException { + /** + * <p> + * Constructs the exception. + * </p> + */ + public MessageStoreEOFException() { + super("The message store has reached EOF"); + } +} diff --git a/src/com/sshtools/j2ssh/transport/Service.java b/src/com/sshtools/j2ssh/transport/Service.java new file mode 100644 index 0000000000000000000000000000000000000000..ca059dd7167a7afadc15db1dde42eb1fdee07361 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/Service.java @@ -0,0 +1,259 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + + +/** + * <p> + * This class implements the transport protocol service. + * </p> + * + * <p> + * After the transport protocol negotiates the protocol version and performs + * server authentication via key exchange, the client requests a service. The + * service is identified by a name and currently there are 2 services defined.<br> + * <br> + * ssh-userauth<br> + * ssh-connection<br> + * <br> + * These 2 services are implemented by the SSH authentication protocol and SSH + * connection protocol respectivley. Further services can be defined and a + * similar local naming policy is applied to the service names, as is applied + * to the algorithm names; a local service should use the + * "servicename(at)domain" syntax. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.42 $ + * + * @since 0.2.0 + */ +public abstract class Service { + private static Log log = LogFactory.getLog(Service.class); + + /** + * Service start mode passed into <code>init</code> method when the service + * is operating in client mode. i.e its requesting a service to be started + * on the remote server and requires a SSH_MSG_SERVICE_ACCEPT message. + */ + public final static int REQUESTING_SERVICE = 1; + + /** + * Serivce start mode passed into <code>init</code> method when the service + * is operating in server mode. i.e a client is requesting a service to be + * started on the local computer and requires the SSH_MSG_SERVICE_ACCEPT + * message to be sent. + */ + public final static int ACCEPTING_SERVICE = 2; + + /** + * The message store registered with the transport protocol to receive the + * service's message. + */ + protected SshMessageStore messageStore = new SshMessageStore(); + + /** The underlying transport protocol */ + protected TransportProtocol transport; + + /** This instances start mode */ + protected Integer startMode = null; + + /** The current state of the service */ + protected ServiceState state = new ServiceState(); + + /** The name of the service */ + private String serviceName; + + /** + * <p> + * Constructs the service. + * </p> + * + * @param serviceName the name of the service + * + * @since 0.2.0 + */ + public Service(String serviceName) { + this.serviceName = serviceName; + } + + /** + * <p> + * Returns the service name. + * </p> + * + * @return the serivce name + * + * @since 0.2.0 + */ + public final String getServiceName() { + return serviceName; + } + + /** + * <p> + * Starts the service. + * </p> + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + public final void start() throws IOException { + if (startMode == null) { + throw new IOException("Service must be initialized first!"); + } + + // If were accepted (i.e. client) we will call onServiceAccept() + if (startMode.intValue() == REQUESTING_SERVICE) { + log.info(serviceName + " has been accepted"); + onServiceAccept(); + } else { + // We've recevied a request instead + log.info(serviceName + " has been requested"); + onServiceRequest(); + } + + onStart(); + state.setValue(ServiceState.SERVICE_STARTED); + } + + /** + * <p> + * Called when the service is started. + * </p> + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + protected abstract void onStart() throws IOException; + + /** + * <p> + * Returns the state of the service. + * </p> + * + * @return the state of the service + * + * @see ServiceState + * @since 0.2.0 + */ + public ServiceState getState() { + return state; + } + + /** + * <p> + * Initialize the service. + * </p> + * + * @param startMode the mode of the service + * @param transport the underlying transport protocol + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + public void init(int startMode, TransportProtocol transport) + throws IOException { + if ((startMode != REQUESTING_SERVICE) && + (startMode != ACCEPTING_SERVICE)) { + throw new IOException("Invalid start mode!"); + } + + this.transport = transport; + this.startMode = new Integer(startMode); + + //this.nativeSettings = nativeSettings; + onServiceInit(startMode); + transport.addMessageStore(messageStore); + } + + /** + * <p> + * Stops the service. + * </p> + * + * @since 0.2.0 + */ + public final void stop() { + messageStore.close(); + state.setValue(ServiceState.SERVICE_STOPPED); + } + + /** + * <p> + * Called when the service is accepted by the remote server. + * </p> + * + * @throws IOException + * + * @since 0.2.0 + */ + protected abstract void onServiceAccept() throws IOException; + + /** + * <p> + * Called when the service is intialized. + * </p> + * + * @param startMode the mode of the service + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + protected abstract void onServiceInit(int startMode) + throws IOException; + + /** + * + * + * @throws IOException + */ + protected abstract void onServiceRequest() throws IOException; + + /** + * <p> + * Sends the SSH_MSG_SERVICE_ACCEPT message to the client to indicate that + * the local computer is accepting the remote computers service request. + * </p> + * + * @throws IOException if an IO error occurs + * + * @since 0.2.0 + */ + protected void sendServiceAccept() throws IOException { + SshMsgServiceAccept msg = new SshMsgServiceAccept(serviceName); + transport.sendMessage(msg, this); + } +} diff --git a/src/com/sshtools/j2ssh/transport/ServiceState.java b/src/com/sshtools/j2ssh/transport/ServiceState.java new file mode 100644 index 0000000000000000000000000000000000000000..cb412a8ecae2089a9050f0f2913f8946208abe87 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/ServiceState.java @@ -0,0 +1,75 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.util.State; + + +/** + * <p> + * This class represents the state of a transport protocol service. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.24 $ + * + * @since 0.2.0 + */ +public class ServiceState extends State { + /** The service is unitialized */ + public final static int SERVICE_UNINITIALIZED = 1; + + /** The service has started and can send/recieve messages */ + public final static int SERVICE_STARTED = 2; + + /** The service has stopped and no messages can be sent or received */ + public final static int SERVICE_STOPPED = 3; + + /** + * <p> + * Constructs the state instance + * </p> + */ + public ServiceState() { + super(SERVICE_UNINITIALIZED); + } + + /** + * <p> + * Evaluates whether the state is valid. + * </p> + * + * @param state + * + * @return + * + * @since 0.2.0 + */ + public boolean isValidState(int state) { + return ((state == SERVICE_UNINITIALIZED) || (state == SERVICE_STARTED) || + (state == SERVICE_STOPPED)); + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMessage.java b/src/com/sshtools/j2ssh/transport/SshMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..b7c5e6209838ea93fc647555f10630b450897cf5 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMessage.java @@ -0,0 +1,188 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + + +/** + * <p> + * This class implements the payload portion each message sent by the transport + * protocol. Each message consists of an integer message id followed by a + * variable byte array containing message data. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.21 $ + * + * @since 0.2.0 + */ +public abstract class SshMessage { + // The message Id of the message + private int messageId; + + /** + * <p> + * Contructs the message. + * </p> + * + * @param messageId the id of the message + * + * @since 0.2.0 + */ + public SshMessage(int messageId) { + // Save the message id + this.messageId = messageId; + } + + /** + * <p> + * Returns the id of the message + * </p> + * + * @return an integer message id + * + * @since 0.2.0 + */ + public final int getMessageId() { + return messageId; + } + + /** + * <p> + * Returns the name of the message implementation for debugging purposes. + * </p> + * + * @return the name of the message e.g. "SSH_MSG_DISCONNECT" + * + * @since 0.2.0 + */ + public abstract String getMessageName(); + + /** + * <p> + * Format the message into the payload array for sending by the transport + * protocol. This implementation creates a byte array, writes the message + * id and calls the abstract <code>constructByteArray</code>. + * </p> + * + * @return the payload portion of a transport protocol message + * + * @throws InvalidMessageException if the message is invalid + * + * @since 0.2.0 + */ + public final byte[] toByteArray() throws InvalidMessageException { + // Create a writer object to construct the array + ByteArrayWriter baw = new ByteArrayWriter(); + + // Write the message id + baw.write(messageId); + + // Call the abstract method so subclasses classes can add their data + constructByteArray(baw); + + // Return the array + return baw.toByteArray(); + } + + /** + * <p> + * Initializes the message from a byte array. + * </p> + * + * @param data the byte array being read. + * + * @throws InvalidMessageException if the message is invalid + * + * @since 0.2.0 + */ + protected final void fromByteArray(ByteArrayReader data) + throws InvalidMessageException { + // Skip the first 5 bytes as this contains the packet length and payload + // length fields + data.skip(5); + + int id = data.read(); + + if (id != messageId) { + throw new InvalidMessageException("The message id " + + String.valueOf(id) + + " is not the same as the message implementation id " + + String.valueOf(messageId)); + } + + // Call abstract method for subclasses to extract the message specific data + constructMessage(data); + } + + /** + * <p> + * Helper method to extract the message id from the complete message data + * recieved by the transport protocol. + * </p> + * + * @param msgdata the transport protocol message + * + * @return the id of the message + * + * @since 0.2.0 + */ + public static Integer getMessageId(byte[] msgdata) { + return new Integer(msgdata[5]); + } + + /** + * <p> + * Message implementations should implement this method, writing the data + * as exected in the transport protocol message format. + * </p> + * + * @param baw the byte array being written to + * + * @throws InvalidMessageException if the message is invalid + * + * @since 0.2.0 + */ + protected abstract void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException; + + /** + * <p> + * Message implementation should implement this method, reading the data as + * expected in the transport protocol message format. + * </p> + * + * @param bar the byte array being read + * + * @throws InvalidMessageException if the message is invalid + * + * @since 0.2.0 + */ + protected abstract void constructMessage(ByteArrayReader bar) + throws InvalidMessageException; +} diff --git a/src/com/sshtools/j2ssh/transport/SshMessageListener.java b/src/com/sshtools/j2ssh/transport/SshMessageListener.java new file mode 100644 index 0000000000000000000000000000000000000000..47ffeb67bc1c7a8642e6979029ad92cb7e321fc4 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMessageListener.java @@ -0,0 +1,39 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p>Title: </p> + * <p>Description: </p> + * <p>Copyright: Copyright (c) 2003</p> + * <p>Company: </p> + * @author Lee David Painter + * @version $Id: SshMessageListener.java,v 1.6 2003/09/11 15:35:15 martianx Exp $ + */ +public interface SshMessageListener { + public void messageReceived(SshMessage msg); +} diff --git a/src/com/sshtools/j2ssh/transport/SshMessageStore.java b/src/com/sshtools/j2ssh/transport/SshMessageStore.java new file mode 100644 index 0000000000000000000000000000000000000000..0c5f346a6259f2dd1b4ea61511b6801b87058128 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMessageStore.java @@ -0,0 +1,622 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + + +/** + * <p> + * This class implements a message store that can be used to provide a blocking + * mechanism for transport protocol messages. + * </p> + * + * @author Lee David Painter + * @version $Revision: 1.42 $ + * + * @since 0.2.0 + */ +public final class SshMessageStore { + private static Log log = LogFactory.getLog(SshMessageStore.class); + + // List to hold messages as they are received + private List messages = new ArrayList(); + private Map register = new HashMap(); + private boolean isClosed = false; + private int[] singleIdFilter = new int[1]; + private int interrupt = 5000; + private Vector listeners = new Vector(); + + /** + * <p> + * Contructs the message store. + * </p> + * + * @since 0.2.0 + */ + public SshMessageStore() { + } + + /** + * <p> + * Evaluate whether the message store is closed. + * </p> + * + * @return + * + * @since 0.2.0 + */ + public boolean isClosed() { + return isClosed; + } + + public void addMessageListener(SshMessageListener listener) { + synchronized (listeners) { + listeners.add(listener); + } + } + + /** + * <p> + * Get a message from the store. This method will block until a message + * with an id matching the supplied filter arrives, or the message store + * closes. The message is removed from the store. + * </p> + * + * @param messageIdFilter an array of message ids that are acceptable + * + * @return the next available message + * + * @throws MessageStoreEOFException if the message store is closed + * @throws InterruptedException if the thread was interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage getMessage(int[] messageIdFilter) + throws MessageStoreEOFException, InterruptedException { + try { + return getMessage(messageIdFilter, 0); + } catch (MessageNotAvailableException e) { + // This should never happen but throw just in case + throw new MessageStoreEOFException(); + } + } + + /** + * <p> + * Get a message from the store. This method will block until a message + * with an id matching the supplied filter arrives, the specified timeout + * is reached or the message store closes. The message is removed from the + * store. + * </p> + * + * @param messageIdFilter an array of message ids that are acceptable. + * @param timeout the maximum number of milliseconds to block before + * returning. + * + * @return the next available message + * + * @throws MessageStoreEOFException if the message store is closed + * @throws MessageNotAvailableException if the message is not available + * after a timeout + * @throws InterruptedException if the thread is interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage getMessage(int[] messageIdFilter, int timeout) + throws MessageStoreEOFException, MessageNotAvailableException, + InterruptedException { + if ((messages.size() <= 0) && isClosed) { + throw new MessageStoreEOFException(); + } + + if (messageIdFilter == null) { + return nextMessage(); + } + + SshMessage msg; + boolean firstPass = true; + + if (timeout < 0) { + timeout = 0; + } + + while ((messages.size() > 0) || !isClosed) { + // lookup the message + msg = lookupMessage(messageIdFilter, true); + + if (msg != null) { + return msg; + } else { + // If this is the second time and there's no message, then throw + if (!firstPass && (timeout > 0)) { + throw new MessageNotAvailableException(); + } + } + + // Now wait + if (!isClosed) { + wait((timeout == 0) ? interrupt : timeout); + } + + firstPass = false; + } + + throw new MessageStoreEOFException(); + } + + /** + * <p> + * Get a message from the store. This method will block until a message + * with an id matching the supplied id arrives, or the message store + * closes. The message is removed from the store. + * </p> + * + * @param messageId the id of the message requried + * + * @return the next available message with the id supplied + * + * @throws MessageStoreEOFException if the message store closed + * @throws InterruptedException if the thread is interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage getMessage(int messageId) + throws MessageStoreEOFException, InterruptedException { + try { + return getMessage(messageId, 0); + } catch (MessageNotAvailableException e) { + // This should never happen by throw jsut in case + throw new MessageStoreEOFException(); + } + } + + /** + * <p> + * Get a message from the store. This method will block until a message + * with an id matching the supplied id arrives,the specified timeout is + * reached or the message store closes. The message will be removed from + * the store. + * </p> + * + * @param messageId the id of the message requried + * @param timeout the maximum number of milliseconds to block before + * returning. + * + * @return the next available message with the id supplied + * + * @throws MessageStoreEOFException if the message store closed + * @throws InterruptedException if the thread is interrupted + * @throws InterruptedException + * + * @since 0.2.0 + */ + public synchronized SshMessage getMessage(int messageId, int timeout) + throws MessageStoreEOFException, MessageNotAvailableException, + InterruptedException { + singleIdFilter[0] = messageId; + + return getMessage(singleIdFilter, timeout); + } + + /** + * <p> + * Evaluate whether the store has any messages. + * </p> + * + * @return true if messages exist, otherwise false + * + * @since 0.2.0 + */ + public boolean hasMessages() { + return messages.size() > 0; + } + + /** + * <p> + * Returns the number of messages contained within this message store. + * </p> + * + * @return the number of messages + * + * @since 0.2.0 + */ + public int size() { + return messages.size(); + } + + /** + * <p> + * Determines if the message id is a registered message of this store. + * </p> + * + * @param messageId the message id + * + * @return true if the message id is registered, otherwise false + * + * @since 0.2.0 + */ + public boolean isRegisteredMessage(Integer messageId) { + return register.containsKey(messageId); + } + + /** + * <p> + * Adds a raw message to the store and processes the data into a registered + * message. + * </p> + * + * @param msgdata the raw message data to process + * + * @throws MessageNotRegisteredException if the message id of the raw data + * is not a registered message + * @throws InvalidMessageException if the message is invalid + * + * @since 0.2.0 + */ + public void addMessage(byte[] msgdata) + throws MessageNotRegisteredException, InvalidMessageException { + Integer messageId = new Integer(msgdata[5]); + + if (!isRegisteredMessage(messageId)) { + throw new MessageNotRegisteredException(messageId); + } + + Class cls = (Class) register.get(SshMessage.getMessageId(msgdata)); + + try { + SshMessage msg = (SshMessage) cls.newInstance(); + msg.fromByteArray(new ByteArrayReader(msgdata)); + addMessage(msg); + } catch (IllegalAccessException iae) { + throw new InvalidMessageException( + "Illegal access for implementation class " + cls.getName()); + } catch (InstantiationException ie) { + throw new InvalidMessageException("Instantiation failed for class " + + cls.getName()); + } + } + + /** + * <p> + * Add a formed message to the store. + * </p> + * + * @param msg the message to add to the store + * + * @throws MessageNotRegisteredException if the message type is not + * registered with the store + * + * @since 0.2.0 + */ + public synchronized void addMessage(SshMessage msg) + throws MessageNotRegisteredException { + // Add the message + messages.add(messages.size(), msg); + + synchronized (listeners) { + if (listeners.size() > 0) { + for (Iterator it = listeners.iterator(); it.hasNext();) { + ((SshMessageListener) it.next()).messageReceived(msg); + } + } + } + + // Notify the threads + notifyAll(); + } + + /** + * <p> + * Closes the store. This will cause any blocking operations on the message + * store to return. + * </p> + * + * @since 0.2.0 + */ + public synchronized void close() { + isClosed = true; + + // We need to notify all anyway as if there are messages still available + // it should not affect the waiting threads as they are waiting for their + // own messages to be received because non were avaialable in the first place + //if (messages.size()<=0) { + notifyAll(); + + //} + } + + /** + * <p> + * Get the next message in the store or wait until a new message arrives. + * The message is removed from the store. + * </p> + * + * @return the next available message. + * + * @throws MessageStoreEOFException if the message store is closed + * @throws InterruptedException if the thread is interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage nextMessage() + throws MessageStoreEOFException, InterruptedException { + if ((messages.size() <= 0) && isClosed) { + throw new MessageStoreEOFException(); + } + + // If there are no messages available then wait untill there are. + while ((messages.size() <= 0) && !isClosed) { + wait(interrupt); + } + + if (messages.size() > 0) { + return (SshMessage) messages.remove(0); + } else { + throw new MessageStoreEOFException(); + } + } + + /** + * + */ + public synchronized void breakWaiting() { + notifyAll(); + } + + /** + * <p> + * Get a message from the store without removing or blocking if the message + * does not exist. + * </p> + * + * @param messageIdFilter the id of the message requried + * + * @return the next available message with the id supplied + * + * @throws MessageStoreEOFException if the message store closed + * @throws MessageNotAvailableException if the message is not available + * @throws InterruptedException if the thread is interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage peekMessage(int[] messageIdFilter) + throws MessageStoreEOFException, MessageNotAvailableException, + InterruptedException { + return peekMessage(messageIdFilter, 0); + } + + /** + * <p> + * Get a message from the store without removing it; only blocking for the + * number of milliseconds specified in the timeout field. If timeout is + * zero, the method will not block. + * </p> + * + * @param messageIdFilter an array of acceptable message ids + * @param timeout the number of milliseconds to wait + * + * @return the next available message of the acceptable message ids + * + * @throws MessageStoreEOFException if the message store is closed + * @throws MessageNotAvailableException if the message is not available + * @throws InterruptedException if the thread is interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage peekMessage(int[] messageIdFilter, + int timeout) + throws MessageStoreEOFException, MessageNotAvailableException, + InterruptedException { + SshMessage msg; + + // Do a straight lookup + msg = lookupMessage(messageIdFilter, false); + + if (msg != null) { + return msg; + } + + // If were willing to wait the wait and look again + if (timeout > 0) { + if (log.isDebugEnabled()) { + log.debug("No message so waiting for " + + String.valueOf(timeout) + " milliseconds"); + } + + wait(timeout); + msg = lookupMessage(messageIdFilter, false); + + if (msg != null) { + return msg; + } + } + + // Nothing even after a wait so throw the relevant exception + if (isClosed) { + throw new MessageStoreEOFException(); + } else { + throw new MessageNotAvailableException(); + } + } + + private SshMessage lookupMessage(int[] messageIdFilter, boolean remove) { + SshMessage msg; + + for (int x = 0; x < messages.size(); x++) { + msg = (SshMessage) messages.get(x); + + // Determine whether its one of the filtered messages + for (int i = 0; i < messageIdFilter.length; i++) { + if (msg.getMessageId() == messageIdFilter[i]) { + if (remove) { + messages.remove(msg); + } + + return msg; + } + } + } + + return null; + } + + /** + * <p> + * Get a message from the store without removing it. + * </p> + * + * @param messageId the acceptable message id + * + * @return the next available message. + * + * @throws MessageStoreEOFException if the message store is closed. + * @throws MessageNotAvailableException if the message is not available. + * @throws InterruptedException if the thread is interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage peekMessage(int messageId) + throws MessageStoreEOFException, MessageNotAvailableException, + InterruptedException { + return peekMessage(messageId, 0); + } + + /** + * <p> + * Removes a message from the message store. + * </p> + * + * @param msg the message to remove + * + * @since 0.2.0 + */ + public synchronized void removeMessage(SshMessage msg) { + messages.remove(msg); + } + + /** + * <p> + * Get a message from the store without removing it, only blocking for the + * number of milliseconds specified in the timeout field. + * </p> + * + * @param messageId the acceptable message id + * @param timeout the timeout setting in milliseconds + * + * @return the next available message + * + * @throws MessageStoreEOFException if the message store is closed + * @throws MessageNotAvailableException if the message is not available + * @throws InterruptedException if the thread is interrupted + * + * @since 0.2.0 + */ + public synchronized SshMessage peekMessage(int messageId, int timeout) + throws MessageStoreEOFException, MessageNotAvailableException, + InterruptedException { + singleIdFilter[0] = messageId; + + return peekMessage(singleIdFilter, timeout); + } + + /** + * <p> + * Register a message implementation with the store. + * </p> + * + * @param messageId the id of the message + * @param implementor the class of the implementation + * + * @since 0.2.0 + */ + public void registerMessage(int messageId, Class implementor) { + Integer id = new Integer(messageId); + register.put(id, implementor); + } + + /** + * <p> + * Returns an Object array (Integers) of the registered message ids. + * </p> + * + * @return the registered message id array + * + * @since 0.2.0 + */ + public Object[] getRegisteredMessageIds() { + return register.keySet().toArray(); + } + + /** + * <p> + * Create a formed message from raw message data. + * </p> + * + * @param msgdata the raw message data + * + * @return the formed message + * + * @throws MessageNotRegisteredException if the message is not a registered + * message + * @throws InvalidMessageException if the message is invalid + * + * @since 0.2.0 + */ + public SshMessage createMessage(byte[] msgdata) + throws MessageNotRegisteredException, InvalidMessageException { + Integer messageId = SshMessage.getMessageId(msgdata); + + if (!isRegisteredMessage(messageId)) { + throw new MessageNotRegisteredException(messageId); + } + + Class cls = (Class) register.get(SshMessage.getMessageId(msgdata)); + + try { + SshMessage msg = (SshMessage) cls.newInstance(); + msg.fromByteArray(new ByteArrayReader(msgdata)); + + return msg; + } catch (IllegalAccessException iae) { + throw new InvalidMessageException( + "Illegal access for implementation class " + cls.getName()); + } catch (InstantiationException ie) { + throw new InvalidMessageException("Instantiation failed for class " + + cls.getName()); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgDebug.java b/src/com/sshtools/j2ssh/transport/SshMsgDebug.java new file mode 100644 index 0000000000000000000000000000000000000000..74e55b6b69320060d24d66f069caedb2e82b74e9 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgDebug.java @@ -0,0 +1,151 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgDebug extends SshMessage { + /** */ + protected final static int SSH_MSG_DEBUG = 4; + + // Holds the language_tag value + private String langTag; + + // Holds the message value + private String message; + + // Holds the always_display value + private boolean alwaysDisplay; + + /** + * Creates a new SshMsgDebug object. + * + * @param alwaysDisplay + * @param message + * @param langTag + */ + public SshMsgDebug(boolean alwaysDisplay, String message, String langTag) { + super(SSH_MSG_DEBUG); + + // Save the debug details + this.alwaysDisplay = alwaysDisplay; + this.message = message; + this.langTag = langTag; + } + + /** + * Creates a new SshMsgDebug object. + */ + public SshMsgDebug() { + super(SSH_MSG_DEBUG); + } + + /** + * + * + * @return + */ + public boolean getDisplayAlways() { + return alwaysDisplay; + } + + /** + * + * + * @return + */ + public String getLanguageTag() { + return langTag; + } + + /** + * + * + * @return + */ + public String getMessage() { + return message; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_DEBUG"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + // Write the data + baw.write(alwaysDisplay ? 1 : 0); + baw.writeString(message); + baw.writeString(langTag); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing message data: " + + ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + // Extract the message information + alwaysDisplay = (bar.read() == 0) ? false : true; + message = bar.readString(); + langTag = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading message data: " + + ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgDisconnect.java b/src/com/sshtools/j2ssh/transport/SshMsgDisconnect.java new file mode 100644 index 0000000000000000000000000000000000000000..9989d994f5041b802271cfb23fe24777a13f09da --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgDisconnect.java @@ -0,0 +1,195 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgDisconnect extends SshMessage { + /** */ + protected final static int SSH_MSG_DISCONNECT = 1; + + /** */ + public final static int HOST_NOT_ALLOWED = 1; + + /** */ + public final static int PROTOCOL_ERROR = 2; + + /** */ + public final static int KEY_EXCHANGE_FAILED = 3; + + /** */ + public final static int RESERVED = 4; + + /** */ + public final static int MAC_ERROR = 5; + + /** */ + public final static int COMPRESSION_ERROR = 6; + + /** */ + public final static int SERVICE_NOT_AVAILABLE = 7; + + /** */ + public final static int PROTOCOL_VERSION_NOT_SUPPORTED = 8; + + /** */ + public final static int HOST_KEY_NOT_VERIFIABLE = 9; + + /** */ + public final static int CONNECTION_LOST = 10; + + /** */ + public final static int BY_APPLICATION = 11; + + /** */ + public final static int TOO_MANY_CONNECTIONS = 12; + + /** */ + public final static int AUTH_CANCELLED_BY_USER = 13; + + /** */ + public final static int NO_MORE_AUTH_METHODS_AVAILABLE = 14; + + /** */ + public final static int ILLEGAL_USER_NAME = 15; + + // The readble version of the disconneciton reason + private String desc; + + // The language tag + private String langTag; + + // Holds the reason for disconnection + private int reasonCode; + + /** + * Creates a new SshMsgDisconnect object. + * + * @param reasonCode + * @param desc + * @param langTag + */ + public SshMsgDisconnect(int reasonCode, String desc, String langTag) { + super(SSH_MSG_DISCONNECT); + + // Store the message values + this.reasonCode = reasonCode; + this.desc = desc; + this.langTag = langTag; + } + + /** + * Creates a new SshMsgDisconnect object. + */ + public SshMsgDisconnect() { + super(SSH_MSG_DISCONNECT); + } + + /** + * + * + * @return + */ + public String getDescription() { + return desc; + } + + /** + * + * + * @return + */ + public String getLanguageTag() { + return langTag; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_DISCONNECT"; + } + + /** + * + * + * @return + */ + public int getReasonCode() { + return reasonCode; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(reasonCode); + baw.writeString(desc); + baw.writeString(langTag); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing message data: " + + ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + // Save the values + reasonCode = (int) bar.readInt(); + desc = bar.readString(); + langTag = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading message data: " + + ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgIgnore.java b/src/com/sshtools/j2ssh/transport/SshMsgIgnore.java new file mode 100644 index 0000000000000000000000000000000000000000..441a0b3f983c5aa77ba7e84607ddcfb25eb5e302 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgIgnore.java @@ -0,0 +1,113 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgIgnore extends SshMessage { + /** */ + protected final static int SSH_MSG_IGNORE = 2; + private String data; + + /** + * Creates a new SshMsgIgnore object. + * + * @param data + */ + public SshMsgIgnore(String data) { + super(SSH_MSG_IGNORE); + this.data = data; + } + + /** + * Creates a new SshMsgIgnore object. + */ + public SshMsgIgnore() { + super(SSH_MSG_IGNORE); + } + + /** + * + * + * @return + */ + public String getData() { + return data; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_IGNORE"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(data); + } catch (IOException ioe) { + throw new InvalidMessageException( + "Error occurred writing message data: " + ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + data = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException( + "Error occurred reading message data: " + ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgKexInit.java b/src/com/sshtools/j2ssh/transport/SshMsgKexInit.java new file mode 100644 index 0000000000000000000000000000000000000000..3f6dda922543c2133ec94621e3918c441bb2aa76 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgKexInit.java @@ -0,0 +1,352 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.SshConnectionProperties; +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.cipher.SshCipherFactory; +import com.sshtools.j2ssh.transport.compression.SshCompressionFactory; +import com.sshtools.j2ssh.transport.hmac.SshHmacFactory; +import com.sshtools.j2ssh.transport.kex.SshKeyExchangeFactory; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.StringTokenizer; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.25 $ + */ +public class SshMsgKexInit extends SshMessage { + /** */ + protected final static int SSH_MSG_KEX_INIT = 20; + private List supportedCompCS; + private List supportedCompSC; + private List supportedEncryptCS; + private List supportedEncryptSC; + private List supportedKex; + private List supportedLangCS; + private List supportedLangSC; + private List supportedMacCS; + private List supportedMacSC; + private List supportedPK; + + // Message values + private byte[] cookie; + private boolean firstKexFollows; + + /** + * Creates a new SshMsgKexInit object. + */ + public SshMsgKexInit() { + super(SSH_MSG_KEX_INIT); + } + + /** + * Creates a new SshMsgKexInit object. + * + * @param props + */ + public SshMsgKexInit(SshConnectionProperties props) { + super(SSH_MSG_KEX_INIT); + + // Create some random data + cookie = new byte[16]; + + // Seed the random number generator + Random r = ConfigurationLoader.getRND(); + + // Get the next random bytes into our cookie + r.nextBytes(cookie); + + // Get the supported algorithms from the factory objects but adding the + // preffered algorithm to the top of the list + supportedKex = sortAlgorithmList(SshKeyExchangeFactory.getSupportedKeyExchanges(), + props.getPrefKex()); + supportedPK = sortAlgorithmList(SshKeyPairFactory.getSupportedKeys(), + props.getPrefPublicKey()); + supportedEncryptCS = sortAlgorithmList(SshCipherFactory.getSupportedCiphers(), + props.getPrefCSEncryption()); + supportedEncryptSC = sortAlgorithmList(SshCipherFactory.getSupportedCiphers(), + props.getPrefSCEncryption()); + supportedMacCS = sortAlgorithmList(SshHmacFactory.getSupportedMacs(), + props.getPrefCSMac()); + supportedMacSC = sortAlgorithmList(SshHmacFactory.getSupportedMacs(), + props.getPrefSCMac()); + supportedCompCS = sortAlgorithmList(SshCompressionFactory.getSupportedCompression(), + props.getPrefCSComp()); + supportedCompSC = sortAlgorithmList(SshCompressionFactory.getSupportedCompression(), + props.getPrefSCComp()); + + // We currently don't support language preferences + supportedLangCS = new ArrayList(); + supportedLangSC = new ArrayList(); + + // We don't guess (I don't see the point of this in the protocol!) + firstKexFollows = false; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_KEX_INIT"; + } + + /** + * + * + * @return + */ + public List getSupportedCSComp() { + return supportedCompCS; + } + + /** + * + * + * @return + */ + public List getSupportedCSEncryption() { + return supportedEncryptCS; + } + + /** + * + * + * @return + */ + public List getSupportedCSMac() { + return supportedMacCS; + } + + /** + * + * + * @return + */ + public List getSupportedKex() { + return supportedKex; + } + + /** + * + * + * @param pks + */ + public void setSupportedPK(List pks) { + supportedPK.clear(); + supportedPK.addAll(pks); + sortAlgorithmList(supportedPK, SshKeyPairFactory.getDefaultPublicKey()); + } + + /** + * + * + * @return + */ + public List getSupportedPublicKeys() { + return supportedPK; + } + + /** + * + * + * @return + */ + public List getSupportedSCComp() { + return supportedCompSC; + } + + /** + * + * + * @return + */ + public List getSupportedSCEncryption() { + return supportedEncryptSC; + } + + /** + * + * + * @return + */ + public List getSupportedSCMac() { + return supportedMacSC; + } + + /** + * + * + * @param list + * + * @return + */ + public String createDelimString(List list) { + // Set up the seperator (blank to start cause we dont want a comma + // at the beginning of the list) + String sep = ""; + String ret = ""; + + // Iterate through the list + Iterator it = list.iterator(); + + while (it.hasNext()) { + // Add the seperator and then the item + ret += (sep + (String) it.next()); + sep = ","; + } + + return ret; + } + + /** + * + * + * @return + */ + public String toString() { + String ret = "SshMsgKexInit:\n"; + ret += ("Supported Kex " + supportedKex.toString() + "\n"); + ret += ("Supported Public Keys " + supportedPK.toString() + "\n"); + ret += ("Supported Encryption Client->Server " + + supportedEncryptCS.toString() + "\n"); + ret += ("Supported Encryption Server->Client " + + supportedEncryptSC.toString() + "\n"); + ret += ("Supported Mac Client->Server " + supportedMacCS.toString() + + "\n"); + ret += ("Supported Mac Server->Client " + supportedMacSC.toString() + + "\n"); + ret += ("Supported Compression Client->Server " + + supportedCompCS.toString() + "\n"); + ret += ("Supported Compression Server->Client " + + supportedCompSC.toString() + "\n"); + ret += ("Supported Languages Client->Server " + + supportedLangCS.toString() + "\n"); + ret += ("Supported Languages Server->Client " + + supportedLangSC.toString() + "\n"); + ret += ("First Kex Packet Follows [" + + (firstKexFollows ? "TRUE]" : "FALSE]")); + + return ret; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.write(cookie); + baw.writeString(createDelimString(supportedKex)); + baw.writeString(createDelimString(supportedPK)); + baw.writeString(createDelimString(supportedEncryptCS)); + baw.writeString(createDelimString(supportedEncryptSC)); + baw.writeString(createDelimString(supportedMacCS)); + baw.writeString(createDelimString(supportedMacSC)); + baw.writeString(createDelimString(supportedCompCS)); + baw.writeString(createDelimString(supportedCompSC)); + baw.writeString(createDelimString(supportedLangCS)); + baw.writeString(createDelimString(supportedLangSC)); + baw.write((firstKexFollows ? 1 : 0)); + baw.writeInt(0); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing message data: " + + ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + cookie = new byte[16]; + bar.read(cookie); + supportedKex = loadListFromString(bar.readString()); + supportedPK = loadListFromString(bar.readString()); + supportedEncryptCS = loadListFromString(bar.readString()); + supportedEncryptSC = loadListFromString(bar.readString()); + supportedMacCS = loadListFromString(bar.readString()); + supportedMacSC = loadListFromString(bar.readString()); + supportedCompCS = loadListFromString(bar.readString()); + supportedCompSC = loadListFromString(bar.readString()); + supportedLangCS = loadListFromString(bar.readString()); + supportedLangSC = loadListFromString(bar.readString()); + firstKexFollows = (bar.read() == 0) ? false : true; + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading message data: " + + ioe.getMessage()); + } + } + + private List loadListFromString(String str) { + // Create a tokeizer object + StringTokenizer tok = new StringTokenizer(str, ","); + List ret = new ArrayList(); + + // Iterate through the tokens adding the items to the list + while (tok.hasMoreElements()) { + ret.add(tok.nextElement()); + } + + return ret; + } + + private List sortAlgorithmList(List list, String pref) { + if (list.contains(pref)) { + // Remove the prefered from the list wherever it may be + list.remove(pref); + + // Add it to the beginning of the list + list.add(0, pref); + } + + return list; + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgNewKeys.java b/src/com/sshtools/j2ssh/transport/SshMsgNewKeys.java new file mode 100644 index 0000000000000000000000000000000000000000..b2095dc5be8ae96e268b035b09485324f15056d5 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgNewKeys.java @@ -0,0 +1,79 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgNewKeys extends SshMessage { + /** */ + protected final static int SSH_MSG_NEWKEYS = 21; + + /** + * Creates a new SshMsgNewKeys object. + */ + public SshMsgNewKeys() { + super(SSH_MSG_NEWKEYS); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_NEWKEYS"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgServiceAccept.java b/src/com/sshtools/j2ssh/transport/SshMsgServiceAccept.java new file mode 100644 index 0000000000000000000000000000000000000000..3c193d54d2da51ecf07fd268473fd389c7a9c55d --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgServiceAccept.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class SshMsgServiceAccept extends SshMessage { + /** */ + protected final static int SSH_MSG_SERVICE_ACCEPT = 6; + private String serviceName; + + /** + * Creates a new SshMsgServiceAccept object. + * + * @param serviceName + */ + public SshMsgServiceAccept(String serviceName) { + super(SSH_MSG_SERVICE_ACCEPT); + this.serviceName = serviceName; + } + + /** + * Creates a new SshMsgServiceAccept object. + */ + public SshMsgServiceAccept() { + super(SSH_MSG_SERVICE_ACCEPT); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_SERVICE_ACCEPT"; + } + + /** + * + * + * @return + */ + public String getServiceName() { + return serviceName; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(serviceName); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + if (bar.available() > 0) { + serviceName = bar.readString(); + } else { + serviceName = ""; + } + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgServiceRequest.java b/src/com/sshtools/j2ssh/transport/SshMsgServiceRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..5d91ce5de4fcee11be6bff672fb10a4d2f219166 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgServiceRequest.java @@ -0,0 +1,111 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class SshMsgServiceRequest extends SshMessage { + /** */ + public final static int SSH_MSG_SERVICE_REQUEST = 5; + private String serviceName; + + /** + * Creates a new SshMsgServiceRequest object. + * + * @param serviceName + */ + public SshMsgServiceRequest(String serviceName) { + super(SSH_MSG_SERVICE_REQUEST); + this.serviceName = serviceName; + } + + /** + * Creates a new SshMsgServiceRequest object. + */ + public SshMsgServiceRequest() { + super(SSH_MSG_SERVICE_REQUEST); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_SERVICE_REQUEST"; + } + + /** + * + * + * @return + */ + public String getServiceName() { + return serviceName; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeString(serviceName); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing message data"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + serviceName = bar.readString(); + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading message data"); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/SshMsgUnimplemented.java b/src/com/sshtools/j2ssh/transport/SshMsgUnimplemented.java new file mode 100644 index 0000000000000000000000000000000000000000..e4c2b98b4ce8875c7d76f0e636fa68999df7e9b6 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/SshMsgUnimplemented.java @@ -0,0 +1,115 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshMsgUnimplemented extends SshMessage { + /** */ + protected final static int SSH_MSG_UNIMPLEMENTED = 3; + + // The sequence no of the message + private long sequenceNo; + + /** + * Creates a new SshMsgUnimplemented object. + * + * @param sequenceNo + */ + public SshMsgUnimplemented(long sequenceNo) { + super(SSH_MSG_UNIMPLEMENTED); + this.sequenceNo = sequenceNo; + } + + /** + * Creates a new SshMsgUnimplemented object. + */ + public SshMsgUnimplemented() { + super(SSH_MSG_UNIMPLEMENTED); + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_UNIMPLEMENTED"; + } + + /** + * + * + * @return + */ + public long getSequenceNo() { + return sequenceNo; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeInt(sequenceNo); + } catch (IOException ioe) { + throw new InvalidMessageException( + "Error extracting SSH_MSG_UNIMPLMENTED, expected int value"); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + sequenceNo = bar.readInt(); + } catch (IOException ioe) { + throw new InvalidMessageException( + "Error contructing SSH_MSG_UNIMPLEMENTED, expected int value"); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocol.java b/src/com/sshtools/j2ssh/transport/TransportProtocol.java new file mode 100644 index 0000000000000000000000000000000000000000..4146bca05a173dd944913c25e42ef0fca017ece8 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocol.java @@ -0,0 +1,101 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.25 $ + */ +public interface TransportProtocol { + /** + * + * + * @param description + */ + public void disconnect(String description); + + /** + * + * + * @param store + * + * @throws MessageAlreadyRegisteredException + */ + public void addMessageStore(SshMessageStore store) + throws MessageAlreadyRegisteredException; + + /** + * + * + * @param ms + * @param sender + * + * @throws IOException + */ + public void sendMessage(SshMessage ms, Object sender) + throws IOException; + + /** + * + * + * @param filter + * + * @return + * + * @throws IOException + */ + public SshMessage readMessage(int[] filter) throws IOException; + + /** + * + * + * @return + */ + public byte[] getSessionIdentifier(); + + /** + * + * + * @return + */ + public int getConnectionId(); + + public boolean isConnected(); + + /** + * + * + * @return + */ + public TransportProtocolState getState(); + + public String getUnderlyingProviderDetail(); +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolAlgorithmSync.java b/src/com/sshtools/j2ssh/transport/TransportProtocolAlgorithmSync.java new file mode 100644 index 0000000000000000000000000000000000000000..14590907bcddae24c6159f0d5140d0c8885e26a7 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolAlgorithmSync.java @@ -0,0 +1,130 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.transport.cipher.SshCipher; +import com.sshtools.j2ssh.transport.compression.SshCompression; +import com.sshtools.j2ssh.transport.hmac.SshHmac; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public class TransportProtocolAlgorithmSync { + private static Log log = LogFactory.getLog(TransportProtocolAlgorithmSync.class); + private SshCipher cipher = null; + private SshCompression compression = null; + private SshHmac hmac = null; + private boolean isLocked = false; + + /** + * Creates a new TransportProtocolAlgorithmSync object. + */ + public TransportProtocolAlgorithmSync() { + } + + /** + * + * + * @param cipher + */ + public synchronized void setCipher(SshCipher cipher) { + this.cipher = cipher; + } + + /** + * + * + * @return + */ + public synchronized SshCipher getCipher() { + return cipher; + } + + /** + * + * + * @param compression + */ + public synchronized void setCompression(SshCompression compression) { + this.compression = compression; + } + + /** + * + * + * @return + */ + public synchronized SshCompression getCompression() { + return compression; + } + + /** + * + * + * @param hmac + */ + public synchronized void setHmac(SshHmac hmac) { + this.hmac = hmac; + } + + /** + * + * + * @return + */ + public synchronized SshHmac getHmac() { + return hmac; + } + + /** + * + */ + public synchronized void lock() { + while (isLocked) { + try { + wait(50); + } catch (InterruptedException e) { + } + } + + isLocked = true; + } + + /** + * + */ + public synchronized void release() { + isLocked = false; + notifyAll(); + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolClient.java b/src/com/sshtools/j2ssh/transport/TransportProtocolClient.java new file mode 100644 index 0000000000000000000000000000000000000000..067598c924451d85560246c1e50b1a684939f29a --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolClient.java @@ -0,0 +1,457 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.SshException; +import com.sshtools.j2ssh.transport.cipher.SshCipher; +import com.sshtools.j2ssh.transport.cipher.SshCipherFactory; +import com.sshtools.j2ssh.transport.hmac.SshHmac; +import com.sshtools.j2ssh.transport.hmac.SshHmacFactory; +import com.sshtools.j2ssh.transport.kex.KeyExchangeException; +import com.sshtools.j2ssh.transport.kex.SshKeyExchange; +import com.sshtools.j2ssh.transport.publickey.SshKeyPair; +import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.IOException; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.49 $ + */ +public class TransportProtocolClient extends TransportProtocolCommon { + /** */ + protected SshPublicKey pk; + private HostKeyVerification hosts; + private Map services = new HashMap(); + private SshMessageStore ms = new SshMessageStore(); + + /** + * Creates a new TransportProtocolClient object. + * + * @param hosts + * + * @throws TransportProtocolException + */ + public TransportProtocolClient(HostKeyVerification hosts) + throws TransportProtocolException { + super(); + this.hosts = hosts; + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + public void onMessageReceived(SshMessage msg) throws IOException { + throw new IOException("No messages are registered"); + } + + /** + * + * + * @throws MessageAlreadyRegisteredException + */ + public void registerTransportMessages() + throws MessageAlreadyRegisteredException { + // Setup our private message store, we wont be registering any direct messages + ms.registerMessage(SshMsgServiceAccept.SSH_MSG_SERVICE_ACCEPT, + SshMsgServiceAccept.class); + this.addMessageStore(ms); + } + + /** + * + * + * @param service + * + * @throws IOException + * @throws SshException + */ + public void requestService(Service service) throws IOException { + // Make sure the service is supported + if (service.getState().getValue() != ServiceState.SERVICE_UNINITIALIZED) { + throw new IOException("The service instance must be uninitialized"); + } + + if ((state.getValue() != TransportProtocolState.CONNECTED) && + (state.getValue() != TransportProtocolState.PERFORMING_KEYEXCHANGE)) { + throw new IOException("The transport protocol is not connected"); + } + + try { + state.waitForState(TransportProtocolState.CONNECTED); + } catch (InterruptedException ie) { + throw new IOException("The operation was interrupted"); + } + + service.init(Service.REQUESTING_SERVICE, this); // , null); + + // Put the service on our list awaiting acceptance + services.put(service.getServiceName(), service); + + // Create and send the message + SshMessage msg = new SshMsgServiceRequest(service.getServiceName()); + sendMessage(msg, this); + + try { + // Wait for the accept message, if the service is not accepted the + // transport protocol disconencts which should cause an excpetion + msg = ms.getMessage(SshMsgServiceAccept.SSH_MSG_SERVICE_ACCEPT); + } catch (InterruptedException ex) { + throw new SshException( + "The thread was interrupted whilst waiting for a transport protocol message"); + } + + return; + } + + /** + * + */ + protected void onDisconnect() { + Iterator it = services.entrySet().iterator(); + Map.Entry entry; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + ((Service) entry.getValue()).stop(); + } + + services.clear(); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getDecryptionAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedSCEncryption(), + serverKexInit.getSupportedSCEncryption()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getEncryptionAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedCSEncryption(), + serverKexInit.getSupportedCSEncryption()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getInputStreamCompAlgortihm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedSCComp(), + serverKexInit.getSupportedSCComp()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getInputStreamMacAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedSCMac(), + serverKexInit.getSupportedSCMac()); + } + + /** + * + */ + protected void setLocalIdent() { + clientIdent = "SSH-" + PROTOCOL_VERSION + "-" + + SOFTWARE_VERSION_COMMENTS + " [CLIENT]"; + } + + /** + * + * + * @return + */ + public String getLocalId() { + return clientIdent; + } + + /** + * + * + * @param msg + */ + protected void setLocalKexInit(SshMsgKexInit msg) { + log.debug(msg.toString()); + clientKexInit = msg; + } + + /** + * + * + * @return + */ + protected SshMsgKexInit getLocalKexInit() { + return clientKexInit; + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getOutputStreamCompAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedCSComp(), + serverKexInit.getSupportedCSComp()); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getOutputStreamMacAlgorithm() + throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedCSMac(), + serverKexInit.getSupportedCSMac()); + } + + /** + * + * + * @param ident + */ + protected void setRemoteIdent(String ident) { + serverIdent = ident; + } + + /** + * + * + * @return + */ + public String getRemoteId() { + return serverIdent; + } + + /** + * + * + * @param msg + */ + protected void setRemoteKexInit(SshMsgKexInit msg) { + serverKexInit = msg; + } + + /** + * + * + * @return + */ + protected SshMsgKexInit getRemoteKexInit() { + return serverKexInit; + } + + /** + * + * + * @return + */ + public SshPublicKey getServerHostKey() { + return pk; + } + + /** + * + * + * @throws IOException + * @throws TransportProtocolException + */ + protected void onStartTransportProtocol() throws IOException { + while ((state.getValue() != TransportProtocolState.CONNECTED) && + (state.getValue() != TransportProtocolState.DISCONNECTED)) { + try { + state.waitForStateUpdate(); + } catch (InterruptedException ex) { + throw new IOException("The operation was interrupted"); + } + } + + if (state.getValue() == TransportProtocolState.DISCONNECTED) { + if (state.hasError()) { + throw state.getLastError(); + } else { + throw new TransportProtocolException( + "The connection did not complete"); + } + } + } + + /** + * + * + * @param kex + * + * @throws IOException + */ + protected void performKeyExchange(SshKeyExchange kex) + throws IOException { + // Start the key exchange instance + kex.performClientExchange(clientIdent, serverIdent, + clientKexInit.toByteArray(), serverKexInit.toByteArray()); + + // Verify the hoskey + if (!verifyHostKey(kex.getHostKey(), kex.getSignature(), + kex.getExchangeHash())) { + sendDisconnect(SshMsgDisconnect.HOST_KEY_NOT_VERIFIABLE, + "The host key supplied was not valid", + new KeyExchangeException( + "The host key is invalid or was not accepted!")); + } + } + + /** + * + * + * @param encryptCSKey + * @param encryptCSIV + * @param encryptSCKey + * @param encryptSCIV + * @param macCSKey + * @param macSCKey + * + * @throws AlgorithmNotAgreedException + * @throws AlgorithmOperationException + * @throws AlgorithmNotSupportedException + * @throws AlgorithmInitializationException + */ + protected void setupNewKeys(byte[] encryptCSKey, byte[] encryptCSIV, + byte[] encryptSCKey, byte[] encryptSCIV, byte[] macCSKey, + byte[] macSCKey) + throws AlgorithmNotAgreedException, AlgorithmOperationException, + AlgorithmNotSupportedException, AlgorithmInitializationException { + // Setup the encryption cipher + SshCipher sshCipher = SshCipherFactory.newInstance(getEncryptionAlgorithm()); + sshCipher.init(SshCipher.ENCRYPT_MODE, encryptCSIV, encryptCSKey); + algorithmsOut.setCipher(sshCipher); + + // Setup the decryption cipher + sshCipher = SshCipherFactory.newInstance(getDecryptionAlgorithm()); + sshCipher.init(SshCipher.DECRYPT_MODE, encryptSCIV, encryptSCKey); + algorithmsIn.setCipher(sshCipher); + + // Create and put our macs into operation + SshHmac hmac = SshHmacFactory.newInstance(getOutputStreamMacAlgorithm()); + hmac.init(macCSKey); + algorithmsOut.setHmac(hmac); + hmac = SshHmacFactory.newInstance(getInputStreamMacAlgorithm()); + hmac.init(macSCKey); + algorithmsIn.setHmac(hmac); + } + + /** + * + * + * @param key + * @param sig + * @param sigdata + * + * @return + * + * @throws TransportProtocolException + */ + protected boolean verifyHostKey(byte[] key, byte[] sig, byte[] sigdata) + throws TransportProtocolException { + // Determine the public key algorithm and obtain an instance + SshKeyPair pair = SshKeyPairFactory.newInstance(determineAlgorithm( + clientKexInit.getSupportedPublicKeys(), + serverKexInit.getSupportedPublicKeys())); + + // Iniialize the public key instance + pk = pair.setPublicKey(key); + + // We have a valid key so verify it against the allowed hosts + String host; + + try { + InetAddress addr = InetAddress.getByName(properties.getHost()); + + if (!addr.getHostAddress().equals(properties.getHost())) { + host = addr.getHostName() + "," + addr.getHostAddress(); + } else { + host = addr.getHostAddress(); + } + } catch (UnknownHostException ex) { + log.info("The host " + properties.getHost() + + " could not be resolved"); + host = properties.getHost(); + } + + if (!hosts.verifyHost(host, pk)) { + log.info("The host key was not accepted"); + + return false; + } + + boolean result = pk.verifySignature(sig, sigdata); + log.info("The host key signature is " + + (result ? " valid" : "invalid")); + + return result; + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolCommon.java b/src/com/sshtools/j2ssh/transport/TransportProtocolCommon.java new file mode 100644 index 0000000000000000000000000000000000000000..a74503586cd8621e3da4f618527266b2e11e7f7f --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolCommon.java @@ -0,0 +1,1471 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.SshException; +import com.sshtools.j2ssh.SshThread; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.SshConnectionProperties; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.net.TransportProvider; +import com.sshtools.j2ssh.transport.kex.KeyExchangeException; +import com.sshtools.j2ssh.transport.kex.SshKeyExchange; +import com.sshtools.j2ssh.transport.kex.SshKeyExchangeFactory; +import com.sshtools.j2ssh.util.Hash; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import java.math.BigInteger; + +import java.security.NoSuchAlgorithmException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.2 $ + */ +public abstract class TransportProtocolCommon +implements TransportProtocol, Runnable +{ + // Flag to keep on running + //private boolean keepRunning = true; + + /** */ + protected static Log log = LogFactory.getLog(TransportProtocolCommon.class); + private static int nextThreadNo = 1; + + /** */ + public final static int EOL_CRLF = 1; + + /** */ + public final static int EOL_LF = 2; + + /** */ + public static final String PROTOCOL_VERSION = "2.0"; + + /** */ + public static String SOFTWARE_VERSION_COMMENTS = "http://www.sshtools.com " + + ConfigurationLoader.getVersionString("J2SSH", "j2ssh.properties"); + private int threadNo = nextThreadNo++; + + /** */ + protected BigInteger k = null; + + /** */ + protected Boolean completeOnNewKeys = new Boolean(false); + + /** */ + protected HostKeyVerification hosts; + + /** */ + protected Map kexs = new HashMap(); + private boolean sendIgnore = false; + + //protected Map transportMessages = new HashMap(); + + /** */ + protected SshConnectionProperties properties; + + /** */ + protected SshMessageStore messageStore = new SshMessageStore(); + + /** */ + protected SshMsgKexInit clientKexInit = null; + + /** */ + protected SshMsgKexInit serverKexInit = null; + + /** */ + protected String clientIdent = null; + + /** */ + protected String serverIdent = null; + + /** */ + protected TransportProtocolAlgorithmSync algorithmsIn; + + /** */ + protected TransportProtocolAlgorithmSync algorithmsOut; + + /** */ + protected TransportProtocolState state = new TransportProtocolState(); + private byte[] exchangeHash = null; + + /** */ + protected byte[] sessionIdentifier = null; + + /** */ + protected byte[] hostKey = null; + + /** */ + protected byte[] signature = null; + private Vector eventHandlers = new Vector(); + + // Storage of messages whilst in key exchange + private List messageStack = new ArrayList(); + + // Message notification registry + private Map messageNotifications = new HashMap(); + + // Key exchange lock for accessing the kex init messages + private Object kexLock = new Object(); + + // Object to synchronize key changing + private Object keyLock = new Object(); + + // The connected socket + //private Socket socket; + // The underlying transport provider + TransportProvider provider; + + // The thread object + private SshThread thread; + private long kexTimeout = 3600000L; + // private long kexTransferLimitKB = 100L; // 100 K + private long kexTransferLimitKB = 1073741824L/1024L; + // private long kexTransferLimit = 1073741824L; + private long startTime = System.currentTimeMillis(); + private long transferredKB = 0; + private long lastTriggeredKB = 0; + + // The input stream for recieving data + + /** */ + protected TransportProtocolInputStream sshIn; + + // The output stream for sending data + + /** */ + protected TransportProtocolOutputStream sshOut; + private int remoteEOL = EOL_CRLF; + + //private Map registeredMessages = new HashMap(); + private Vector messageStores = new Vector(); + + /** + * Creates a new TransportProtocolCommon object. + */ + public TransportProtocolCommon() { + } + + /** + * + * + * @return + */ + public int getConnectionId() { + return threadNo; + } + + /** + * + * + * @return + */ + public int getRemoteEOL() { + return remoteEOL; + } + + /** + * + * + * @return + */ + public TransportProtocolState getState() { + return state; + } + + /** + * + * + * @return + */ + public SshConnectionProperties getProperties() { + return properties; + } + + /** + * + */ + protected abstract void onDisconnect(); + + /** + * + * + * @param description + */ + public void disconnect(String description) { + if (log.isDebugEnabled()) { + log.debug("Disconnect: " + description); + } + + try { + state.setValue(TransportProtocolState.DISCONNECTED); + state.setDisconnectReason(description); + + // Send the disconnect message automatically + sendDisconnect(SshMsgDisconnect.BY_APPLICATION, description); + } catch (Exception e) { + log.warn("Failed to send disconnect", e); + } + } + + /** + * + * + * @param sendIgnore + */ + public void setSendIgnore(boolean sendIgnore) { + this.sendIgnore = sendIgnore; + } + + /** + * + * + * @param seconds + * + * @throws TransportProtocolException + */ + public void setKexTimeout(long seconds) throws TransportProtocolException { + if (seconds < 60) { + throw new TransportProtocolException( + "Keys can only be re-exchanged every minute or more"); + } + + kexTimeout = seconds * 1000; + } + + /** + * + * + * @param kilobytes + * + * @throws TransportProtocolException + */ + public void setKexTransferLimit(long kilobytes) + throws TransportProtocolException { + if (kilobytes < 10) { + throw new TransportProtocolException( + "Keys can only be re-exchanged after every 10k of data, or more"); + } + + //kexTransferLimit = kilobytes * 1024; + kexTransferLimitKB = kilobytes; + } + + /*public InetSocketAddress getRemoteAddress() { + return (InetSocketAddress)socket.getRemoteSocketAddress(); + }*/ + public long getOutgoingByteCount() { + return sshOut.getNumBytesTransfered(); + } + + /** + * + * + * @return + */ + public long getIncomingByteCount() { + return sshIn.getNumBytesTransfered(); + } + + /** + * + * + * @param eventHandler + */ + public void addEventHandler(TransportProtocolEventHandler eventHandler) { + if (eventHandler != null) { + eventHandlers.add(eventHandler); + } + } + + /** + * + * + * @throws MessageAlreadyRegisteredException + */ + public abstract void registerTransportMessages() + throws MessageAlreadyRegisteredException; + + /** + * + * + * @return + */ + public byte[] getSessionIdentifier() { + return (byte[]) sessionIdentifier.clone(); + } + + /** + * + */ + public void run() { + try { + state.setValue(TransportProtocolState.NEGOTIATING_PROTOCOL); + log.info("Registering transport protocol messages with inputstream"); + algorithmsOut = new TransportProtocolAlgorithmSync(); + algorithmsIn = new TransportProtocolAlgorithmSync(); + + // Create the input/output streams + sshIn = new TransportProtocolInputStream(this, + provider.getInputStream(), algorithmsIn); + sshOut = new TransportProtocolOutputStream(provider.getOutputStream(), + this, algorithmsOut); + + // Register the transport layer messages that this class will handle + messageStore.registerMessage(SshMsgDisconnect.SSH_MSG_DISCONNECT, + SshMsgDisconnect.class); + messageStore.registerMessage(SshMsgIgnore.SSH_MSG_IGNORE, + SshMsgIgnore.class); + messageStore.registerMessage(SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED, + SshMsgUnimplemented.class); + messageStore.registerMessage(SshMsgDebug.SSH_MSG_DEBUG, + SshMsgDebug.class); + messageStore.registerMessage(SshMsgKexInit.SSH_MSG_KEX_INIT, + SshMsgKexInit.class); + messageStore.registerMessage(SshMsgNewKeys.SSH_MSG_NEWKEYS, + SshMsgNewKeys.class); + registerTransportMessages(); + + List list = SshKeyExchangeFactory.getSupportedKeyExchanges(); + Iterator it = list.iterator(); + + while (it.hasNext()) { + String keyExchange = (String) it.next(); + SshKeyExchange kex = SshKeyExchangeFactory.newInstance(keyExchange); + kex.init(this); + kexs.put(keyExchange, kex); + } + + // call abstract to initialise the local ident string + setLocalIdent(); + + // negotiate the protocol version + negotiateVersion(); + startBinaryPacketProtocol(); + } + catch (Throwable e) { + if (e instanceof IOException) { + state.setLastError((IOException) e); + } + + if (state.getValue() != TransportProtocolState.DISCONNECTED) { + log.error("The Transport Protocol thread failed", e); + + //log.info(e.getMessage()); + stop(); + } + } + finally { + thread = null; + } + + log.debug("The Transport Protocol has been stopped"); + } + + /** + * + * + * @param msg + * @param sender + * + * @throws IOException + * @throws TransportProtocolException + */ + public synchronized void sendMessage(SshMessage msg, Object sender) + throws IOException { + // Send a message, if were in key exchange then add it to + // the list unless of course it is a transport protocol or key + // exchange message + if (log.isDebugEnabled()) { + log.info("Sending " + msg.getMessageName()); + } + + int currentState = state.getValue(); + + if (sender instanceof SshKeyExchange || + sender instanceof TransportProtocolCommon || + (currentState == TransportProtocolState.CONNECTED)) { + sshOut.sendMessage(msg); + + if (currentState == TransportProtocolState.CONNECTED) { + if (sendIgnore) { + byte[] count = new byte[1]; + ConfigurationLoader.getRND().nextBytes(count); + + byte[] rand = new byte[(count[0] & 0xFF) + 1]; + ConfigurationLoader.getRND().nextBytes(rand); + + SshMsgIgnore ignore = new SshMsgIgnore(new String(rand)); + + if (log.isDebugEnabled()) { + log.debug("Sending " + ignore.getMessageName()); + } + + sshOut.sendMessage(ignore); + } + } + } else if (currentState == TransportProtocolState.PERFORMING_KEYEXCHANGE) { + log.debug("Adding to message queue whilst in key exchange"); + + synchronized (messageStack) { + // Add this message to the end of the list + messageStack.add(msg); + } + } else { + throw new TransportProtocolException( + "The transport protocol is disconnected"); + } + } + + /** + * + * + * @throws IOException + */ + protected abstract void onStartTransportProtocol() + throws IOException; + + /** + * + * + * @param provider + * @param properties + * + * @throws IOException + */ + public void startTransportProtocol(TransportProvider provider, + SshConnectionProperties properties) throws IOException { + // Save the connected socket for later use + this.provider = provider; + this.properties = properties; + + // Start the transport layer message loop + log.info("Starting transport protocol"); + thread = new SshThread(this, "Transport protocol", true); + thread.start(); + onStartTransportProtocol(); + } + + /** + * + * + * @return + */ + public String getUnderlyingProviderDetail() { + return provider.getProviderDetail(); + } + + /** + * + * + * @param messageId + * @param store + * + * @throws MessageNotRegisteredException + */ + public void unregisterMessage(Integer messageId, SshMessageStore store) + throws MessageNotRegisteredException { + if (log.isDebugEnabled()) { + log.debug("Unregistering message Id " + messageId.toString()); + } + + if (!messageNotifications.containsKey(messageId)) { + throw new MessageNotRegisteredException(messageId); + } + + SshMessageStore actual = (SshMessageStore) messageNotifications.get(messageId); + + if (!store.equals(actual)) { + throw new MessageNotRegisteredException(messageId, store); + } + + messageNotifications.remove(messageId); + } + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected abstract String getDecryptionAlgorithm() + throws AlgorithmNotAgreedException; + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected abstract String getEncryptionAlgorithm() + throws AlgorithmNotAgreedException; + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected abstract String getInputStreamCompAlgortihm() + throws AlgorithmNotAgreedException; + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected abstract String getInputStreamMacAlgorithm() + throws AlgorithmNotAgreedException; + + /** + * + */ + protected abstract void setLocalIdent(); + + /** + * + * + * @return + */ + public abstract String getLocalId(); + + /** + * + * + * @param msg + */ + protected abstract void setLocalKexInit(SshMsgKexInit msg); + + /** + * + * + * @return + */ + protected abstract SshMsgKexInit getLocalKexInit(); + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected abstract String getOutputStreamCompAlgorithm() + throws AlgorithmNotAgreedException; + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected abstract String getOutputStreamMacAlgorithm() + throws AlgorithmNotAgreedException; + + /** + * + * + * @param ident + */ + protected abstract void setRemoteIdent(String ident); + + /** + * + * + * @return + */ + public abstract String getRemoteId(); + + /** + * + * + * @param msg + */ + protected abstract void setRemoteKexInit(SshMsgKexInit msg); + + /** + * + * + * @return + */ + protected abstract SshMsgKexInit getRemoteKexInit(); + + /** + * + * + * @param kex + * + * @throws IOException + * @throws KeyExchangeException + */ + protected abstract void performKeyExchange(SshKeyExchange kex) + throws IOException, KeyExchangeException; + + /** + * + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String getKexAlgorithm() throws AlgorithmNotAgreedException { + return determineAlgorithm(clientKexInit.getSupportedKex(), + serverKexInit.getSupportedKex()); + } + + public boolean isConnected() { + return (state.getValue() == TransportProtocolState.CONNECTED) || + (state.getValue() == TransportProtocolState.PERFORMING_KEYEXCHANGE); + } + + /** + * + * + * @throws IOException + * @throws KeyExchangeException + */ + protected void beginKeyExchange() throws IOException, KeyExchangeException { + log.info("Starting key exchange"); + + //state.setValue(TransportProtocolState.PERFORMING_KEYEXCHANGE); + String kexAlgorithm = ""; + + // We now have both kex inits, this is where client/server + // implemtations take over so call abstract methods + try { + // Determine the key exchange algorithm + kexAlgorithm = getKexAlgorithm(); + + if (log.isDebugEnabled()) { + log.debug("Key exchange algorithm: " + kexAlgorithm); + } + + // Get an instance of the key exchange algortihm + SshKeyExchange kex = (SshKeyExchange) kexs.get(kexAlgorithm); + + // Do the key exchange + performKeyExchange(kex); + + // Record the output + exchangeHash = kex.getExchangeHash(); + + if (sessionIdentifier == null) { + sessionIdentifier = new byte[exchangeHash.length]; + System.arraycopy(exchangeHash, 0, sessionIdentifier, 0, + sessionIdentifier.length); + thread.setSessionId(sessionIdentifier); + } + + hostKey = kex.getHostKey(); + signature = kex.getSignature(); + k = kex.getSecret(); + + // Send new keys + sendNewKeys(); + kex.reset(); + } catch (AlgorithmNotAgreedException e) { + sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, + "No suitable key exchange algorithm was agreed"); + throw new KeyExchangeException( + "No suitable key exchange algorithm could be agreed."); + } + } + + /** + * + * + * @return + * + * @throws IOException + */ + protected SshMsgKexInit createLocalKexInit() throws IOException { + return new SshMsgKexInit(properties); + } + + /** + * + */ + protected void onCorruptMac() { + log.fatal("Corrupt Mac on Input"); + + // Send a disconnect message + sendDisconnect(SshMsgDisconnect.MAC_ERROR, "Corrupt Mac on input", + new SshException("Corrupt Mac on Imput")); + } + + /** + * + * + * @param msg + * + * @throws IOException + */ + protected abstract void onMessageReceived(SshMessage msg) + throws IOException; + + /** + * + * + * @param reason + * @param description + */ + protected void sendDisconnect(int reason, String description) { + SshMsgDisconnect msg = new SshMsgDisconnect(reason, description, ""); + + try { + sendMessage(msg, this); + stop(); + } catch (Exception e) { + log.warn("Failed to send disconnect", e); + } + } + + /** + * + * + * @param reason + * @param description + * @param error + */ + protected void sendDisconnect(int reason, String description, + IOException error) { + state.setLastError(error); + sendDisconnect(reason, description); + } + + /** + * + * + * @throws IOException + */ + protected void sendKeyExchangeInit() throws IOException { + setLocalKexInit(createLocalKexInit()); + sendMessage(getLocalKexInit(), this); + state.setValue(TransportProtocolState.PERFORMING_KEYEXCHANGE); + } + + /** + * + * + * @throws IOException + */ + protected void sendNewKeys() throws IOException { + // Send new keys + SshMsgNewKeys msg = new SshMsgNewKeys(); + sendMessage(msg, this); + + // Lock the outgoing algorithms so nothing else is sent untill + // weve updated them with the new keys + algorithmsOut.lock(); + + // Do we need to hold the algorithmsOut lock during + // the input message handling below? If not, then the + // lock could be taken just before completeKeyExchange + // or even moved into the completeKeyExchange method. + // We would then not need the try-finally below (which + // is needed for exceptions from eg the readMessage call). + boolean hasReleasedLock = false; + try { + int[] filter = new int[1]; + filter[0] = SshMsgNewKeys.SSH_MSG_NEWKEYS; + msg = (SshMsgNewKeys) readMessage(filter); + + if (log.isDebugEnabled()) { + log.debug("Received " + msg.getMessageName()); + } + + // Release done in completeKeyExchange + hasReleasedLock = true; + completeKeyExchange(); + } + finally { + if( ! hasReleasedLock ) { + algorithmsOut.release(); + } + } + } + + /** + * + * + * @param encryptCSKey + * @param encryptCSIV + * @param encryptSCKey + * @param encryptSCIV + * @param macCSKey + * @param macSCKey + * + * @throws AlgorithmNotAgreedException + * @throws AlgorithmOperationException + * @throws AlgorithmNotSupportedException + * @throws AlgorithmInitializationException + */ + protected abstract void setupNewKeys(byte[] encryptCSKey, + byte[] encryptCSIV, byte[] encryptSCKey, byte[] encryptSCIV, + byte[] macCSKey, byte[] macSCKey) + throws AlgorithmNotAgreedException, AlgorithmOperationException, + AlgorithmNotSupportedException, AlgorithmInitializationException; + + /** + * + * + * @throws IOException + * @throws TransportProtocolException + */ + protected void completeKeyExchange() throws IOException { + log.info("Completing key exchange"); + + boolean hasReleasedLock = false; + try { + // Reset the state variables + //completeOnNewKeys = new Boolean(false); + log.debug("Making keys from key exchange output"); + + // Make the keys + byte[] encryptionKey = makeSshKey('C'); + byte[] encryptionIV = makeSshKey('A'); + byte[] decryptionKey = makeSshKey('D'); + byte[] decryptionIV = makeSshKey('B'); + byte[] sendMac = makeSshKey('E'); + byte[] receiveMac = makeSshKey('F'); + log.debug("Creating algorithm objects"); + setupNewKeys(encryptionKey, encryptionIV, decryptionKey, + decryptionIV, sendMac, receiveMac); + + // Reset the key exchange + clientKexInit = null; + serverKexInit = null; + + //algorithmsIn.release(); + algorithmsOut.release(); + hasReleasedLock = true; + + /* + * Update our state, we can send all packets + * + */ + state.setValue(TransportProtocolState.CONNECTED); + + // Send any outstanding messages + synchronized (messageStack) { + Iterator it = messageStack.iterator(); + log.debug("Sending queued messages"); + + while (it.hasNext()) { + SshMessage msg = (SshMessage) it.next(); + sendMessage(msg, this); + } + + messageStack.clear(); + } + } catch (AlgorithmNotAgreedException anae) { + sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, + "Algorithm not agreed"); + throw new TransportProtocolException( + "The connection was disconnected because an algorithm could not be agreed"); + } catch (AlgorithmNotSupportedException anse) { + sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, + "Application error"); + throw new TransportProtocolException( + "The connection was disconnected because an algorithm class could not be loaded"); + } catch (AlgorithmOperationException aoe) { + sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, + "Algorithm operation error"); + throw new TransportProtocolException( + "The connection was disconnected because" + + " of an algorithm operation error"); + } catch (AlgorithmInitializationException aie) { + sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, + "Algorithm initialization error"); + throw new TransportProtocolException( + "The connection was disconnected because" + + " of an algorithm initialization error"); + } + finally { + if( ! hasReleasedLock ) { + algorithmsOut.release(); + } + } + } + + /** + * + * + * @return + */ + protected List getEventHandlers() { + return eventHandlers; + } + + /** + * + * + * @param clientAlgorithms + * @param serverAlgorithms + * + * @return + * + * @throws AlgorithmNotAgreedException + */ + protected String determineAlgorithm(List clientAlgorithms, + List serverAlgorithms) throws AlgorithmNotAgreedException { + if (log.isDebugEnabled()) { + log.debug("Determine Algorithm"); + log.debug("Client Algorithms: " + clientAlgorithms.toString()); + log.debug("Server Algorithms: " + serverAlgorithms.toString()); + } + + String algorithmClient; + String algorithmServer; + Iterator itClient = clientAlgorithms.iterator(); + + while (itClient.hasNext()) { + algorithmClient = (String) itClient.next(); + + Iterator itServer = serverAlgorithms.iterator(); + + while (itServer.hasNext()) { + algorithmServer = (String) itServer.next(); + + if (algorithmClient.equals(algorithmServer)) { + log.debug("Returning " + algorithmClient); + + return algorithmClient; + } + } + } + + throw new AlgorithmNotAgreedException("Could not agree algorithm"); + } + + /** + * + * + * @throws IOException + */ + protected void startBinaryPacketProtocol() throws IOException { + // Send our Kex Init + sendKeyExchangeInit(); + + SshMessage msg; + + // Perform a transport protocol message loop + while (state.getValue() != TransportProtocolState.DISCONNECTED) { + // Process incoming messages returning any transport protocol + // messages to be handled here + msg = processMessages(); + + if (log.isDebugEnabled()) { + log.debug("Received " + msg.getMessageName()); + } + + switch (msg.getMessageId()) { + case SshMsgKexInit.SSH_MSG_KEX_INIT: { + onMsgKexInit((SshMsgKexInit) msg); + + break; + } + + case SshMsgDisconnect.SSH_MSG_DISCONNECT: { + onMsgDisconnect((SshMsgDisconnect) msg); + + break; + } + + case SshMsgIgnore.SSH_MSG_IGNORE: { + onMsgIgnore((SshMsgIgnore) msg); + + break; + } + + case SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED: { + onMsgUnimplemented((SshMsgUnimplemented) msg); + + break; + } + + case SshMsgDebug.SSH_MSG_DEBUG: { + onMsgDebug((SshMsgDebug) msg); + + break; + } + + default: + onMessageReceived(msg); + } + } + } + + /** + * + */ + protected final void stop() { + onDisconnect(); + + Iterator it = eventHandlers.iterator(); + TransportProtocolEventHandler eventHandler; + + while (it.hasNext()) { + eventHandler = (TransportProtocolEventHandler) it.next(); + eventHandler.onDisconnect(this); + } + + // Close the input/output streams + //sshIn.close(); + if (messageStore != null) { + messageStore.close(); + } + + // 05/01/2003 moiz change begin: + // all close all the registerd messageStores + SshMessageStore ms; + + for (it = messageStores.iterator(); (it != null) && it.hasNext();) { + ms = (SshMessageStore) it.next(); + + try { + ms.close(); + } catch (Exception e) { + } + } + + messageStores.clear(); + + // 05/01/2003 moizd change end: + messageStore = null; + + try { + provider.close(); + } catch (IOException ioe) { + } + state.setValue(TransportProtocolState.DISCONNECTED); + } + + private byte[] makeSshKey(char chr) throws IOException { + try { + // Create the first 20 bytes of key data + ByteArrayWriter keydata = new ByteArrayWriter(); + byte[] data = new byte[20]; + Hash hash = new Hash("SHA"); + + // Put the dh k value + hash.putBigInteger(k); + + // Put in the exchange hash + hash.putBytes(exchangeHash); + + // Put in the character + hash.putByte((byte) chr); + + // Put the exchange hash in again + hash.putBytes(sessionIdentifier); + + // Create the fist 20 bytes + data = hash.doFinal(); + keydata.write(data); + + // Now do the next 20 + hash.reset(); + + // Put the dh k value in again + hash.putBigInteger(k); + + // And the exchange hash + hash.putBytes(exchangeHash); + + // Finally the first 20 bytes of data we created + hash.putBytes(data); + data = hash.doFinal(); + + // Put it all together + keydata.write(data); + + // Return it + return keydata.toByteArray(); + } catch (NoSuchAlgorithmException nsae) { + sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, + "Application error"); + throw new TransportProtocolException("SHA algorithm not supported"); + } catch (IOException ioe) { + sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, + "Application error"); + throw new TransportProtocolException("Error writing key data"); + } + } + + private void negotiateVersion() throws IOException { + byte[] buf; + int len; + String remoteVer = ""; + log.info("Negotiating protocol version"); + log.debug("Local identification: " + getLocalId()); + + // Get the local ident string by calling the abstract method, this + // way the implementations set the correct variables for computing the + // exchange hash + String data = getLocalId() + "\r\n"; + + // Send our version string + provider.getOutputStream().write(data.getBytes()); + + // Now wait for a reply and evaluate the ident string + //buf = new byte[255]; + StringBuffer buffer = new StringBuffer(); + char ch; + int MAX_BUFFER_LENGTH = 255; + + // Look for a string starting with "SSH-" + while (!remoteVer.startsWith("SSH-") && + (buffer.length() < MAX_BUFFER_LENGTH)) { + // Get the next string + while (((ch = (char) provider.getInputStream().read()) != '\n') && + (buffer.length() < MAX_BUFFER_LENGTH)) { + buffer.append(ch); + } + + // Set trimming off any EOL characters + remoteVer = buffer.toString(); + + // Guess the remote sides EOL by looking at the end of the ident string + if (remoteVer.endsWith("\r")) { + remoteEOL = EOL_CRLF; + } else { + remoteEOL = EOL_LF; + } + + log.debug("EOL is guessed at " + + ((remoteEOL == EOL_CRLF) ? "CR+LF" : "LF")); + + // Remove any \r + remoteVer = remoteVer.trim(); + } + + // Get the index of the seperators + int l = remoteVer.indexOf("-"); + int r = remoteVer.indexOf("-", l + 1); + + // Call abstract method so the implementations can set the + // correct member variable + setRemoteIdent(remoteVer.trim()); + + if (log.isDebugEnabled()) { + log.debug("Remote identification: " + getRemoteId()); + } + + // Get the version + String remoteVersion = remoteVer.substring(l + 1, r); + + // Evaluate the version, we only support 2.0 + if (!(remoteVersion.equals("2.0") || (remoteVersion.equals("1.99")))) { + log.fatal( + "The remote computer does not support protocol version 2.0"); + throw new TransportProtocolException( + "The protocol version of the remote computer is not supported!"); + } + + log.info("Protocol negotiation complete"); + } + + private void onMsgDebug(SshMsgDebug msg) { + log.debug(msg.getMessage()); + } + + private void onMsgDisconnect(SshMsgDisconnect msg) + throws IOException { + log.info("The remote computer disconnected: " + msg.getDescription()); + state.setValue(TransportProtocolState.DISCONNECTED); + state.setDisconnectReason(msg.getDescription()); + stop(); + } + + private void onMsgIgnore(SshMsgIgnore msg) { + if (log.isDebugEnabled()) { + log.debug("SSH_MSG_IGNORE with " + + String.valueOf(msg.getData().length()) + " bytes of data"); + } + } + + private void onMsgKexInit(SshMsgKexInit msg) throws IOException { + log.debug("Received remote key exchange init message"); + log.debug(msg.toString()); + + synchronized (kexLock) { + setRemoteKexInit(msg); + + // As either party can initiate a key exchange then we + // must check to see if we have sent our own + if (state.getValue() != TransportProtocolState.PERFORMING_KEYEXCHANGE) { + //if (getLocalKexInit() == null) { + sendKeyExchangeInit(); + } + + //} + beginKeyExchange(); + } + } + + private void onMsgNewKeys(SshMsgNewKeys msg) throws IOException { + // Determine whether we have completed our own + log.debug("Received New Keys"); + //algorithmsIn.lock(); + + synchronized (completeOnNewKeys) { + if (completeOnNewKeys.booleanValue()) { + // We need to take this lock since + // it is released in completeKeyExchange. + algorithmsOut.lock(); + completeKeyExchange(); + } else { + completeOnNewKeys = new Boolean(true); + } + } + } + + private void onMsgUnimplemented(SshMsgUnimplemented msg) { + if (log.isDebugEnabled()) { + log.debug("The message with sequence no " + msg.getSequenceNo() + + " was reported as unimplemented by the remote end."); + } + } + + /** + * + * + * @param filter + * + * @return + * + * @throws IOException + */ + public SshMessage readMessage(int[] filter) throws IOException { + byte[] msgdata = null; + SshMessage msg; + + while (state.getValue() != TransportProtocolState.DISCONNECTED) { + boolean hasmsg = false; + + while (!hasmsg) { + msgdata = sshIn.readMessage(); + hasmsg = true; + } + + Integer messageId = SshMessage.getMessageId(msgdata); + + // First check the filter + for (int i = 0; i < filter.length; i++) { + if (filter[i] == messageId.intValue()) { + if (messageStore.isRegisteredMessage(messageId)) { + return messageStore.createMessage(msgdata); + } else { + SshMessageStore ms = getMessageStore(messageId); + msg = ms.createMessage(msgdata); + + if (log.isDebugEnabled()) { + log.debug("Processing " + msg.getMessageName()); + } + + return msg; + } + } + } + + if (messageStore.isRegisteredMessage(messageId)) { + msg = messageStore.createMessage(msgdata); + + switch (messageId.intValue()) { + case SshMsgDisconnect.SSH_MSG_DISCONNECT: { + onMsgDisconnect((SshMsgDisconnect) msg); + + break; + } + + case SshMsgIgnore.SSH_MSG_IGNORE: { + onMsgIgnore((SshMsgIgnore) msg); + + break; + } + + case SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED: { + onMsgUnimplemented((SshMsgUnimplemented) msg); + + break; + } + + case SshMsgDebug.SSH_MSG_DEBUG: { + onMsgDebug((SshMsgDebug) msg); + + break; + } + + default: // Exception not allowed + throw new IOException( + "Unexpected transport protocol message"); + } + } else { + throw new IOException("Unexpected message received"); + } + } + + throw new IOException("The transport protocol disconnected"); + } + + /** + * + * + * @return + * + * @throws IOException + */ + protected SshMessage processMessages() throws IOException { + byte[] msgdata = null; + SshMessage msg; + SshMessageStore ms; + + while (state.getValue() != TransportProtocolState.DISCONNECTED) { + long currentTime = System.currentTimeMillis(); + + transferredKB = sshIn.getNumBytesTransfered()/1024 + + sshOut.getNumBytesTransfered()/1024; + + long kbLimit = transferredKB - lastTriggeredKB; + + if (((currentTime - startTime) > kexTimeout) || + (kbLimit > kexTransferLimitKB) ) + { + // ((sshIn.getNumBytesTransfered() + + // sshOut.getNumBytesTransfered()) > kexTransferLimit)) { + startTime = currentTime; + lastTriggeredKB = transferredKB; + if (log.isDebugEnabled()) { + log.info("rekey"); + } + sendKeyExchangeInit(); + } + + boolean hasmsg = false; + + while (!hasmsg) { + try { + msgdata = sshIn.readMessage(); + hasmsg = true; + } catch (InterruptedIOException ex /*SocketTimeoutException ex*/) { + log.info("Possible timeout on transport inputstream"); + + Iterator it = eventHandlers.iterator(); + TransportProtocolEventHandler eventHandler; + + while (it.hasNext()) { + eventHandler = (TransportProtocolEventHandler) it.next(); + eventHandler.onSocketTimeout(this /*, + provider.isConnected()*/); + } + } + } + + Integer messageId = SshMessage.getMessageId(msgdata); + + if (!messageStore.isRegisteredMessage(messageId)) { + try { + ms = getMessageStore(messageId); + msg = ms.createMessage(msgdata); + + if (log.isDebugEnabled()) { + log.info("Received " + msg.getMessageName()); + } + + ms.addMessage(msg); + } catch (MessageNotRegisteredException mnre) { + log.info("Unimplemented message received " + + String.valueOf(messageId.intValue())); + msg = new SshMsgUnimplemented(sshIn.getSequenceNo()); + sendMessage(msg, this); + } + } else { + return messageStore.createMessage(msgdata); + } + } + + throw new IOException("The transport protocol has disconnected"); + } + + /** + * + * + * @param store + * + * @throws MessageAlreadyRegisteredException + */ + public void addMessageStore(SshMessageStore store) + throws MessageAlreadyRegisteredException { + messageStores.add(store); + } + + private SshMessageStore getMessageStore(Integer messageId) + throws MessageNotRegisteredException { + SshMessageStore ms; + + for (Iterator it = messageStores.iterator(); + (it != null) && it.hasNext();) { + ms = (SshMessageStore) it.next(); + + if (ms.isRegisteredMessage(messageId)) { + return ms; + } + } + + throw new MessageNotRegisteredException(messageId); + } + + /** + * + * + * @param ms + */ + public void removeMessageStore(SshMessageStore ms) { + messageStores.remove(ms); + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolEventAdapter.java b/src/com/sshtools/j2ssh/transport/TransportProtocolEventAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..18fe1b3ea63d346f2abeaf5b2f9bca0f8be629f8 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolEventAdapter.java @@ -0,0 +1,80 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * <p> + * Title: + * </p> + * + * <p> + * Description: + * </p> + * + * <p> + * Copyright: Copyright (c) 2003 + * </p> + * + * <p> + * Company: + * </p> + * + * @author Lee David Painter + * @version $Id: TransportProtocolEventAdapter.java,v 1.9 2003/09/11 15:35:15 martianx Exp $ + */ +public class TransportProtocolEventAdapter + implements TransportProtocolEventHandler { + /** + * Creates a new TransportProtocolEventAdapter object. + */ + public TransportProtocolEventAdapter() { + } + + /** + * + * + * @param transport + */ + public void onSocketTimeout(TransportProtocol transport) { + } + + /** + * + * + * @param transport + */ + public void onDisconnect(TransportProtocol transport) { + } + + /** + * + * + * @param transport + */ + public void onConnected(TransportProtocol transport) { + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolEventHandler.java b/src/com/sshtools/j2ssh/transport/TransportProtocolEventHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..145eadc1d70b9516b16f1b1782f6f9fde3eacb1d --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolEventHandler.java @@ -0,0 +1,58 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public interface TransportProtocolEventHandler { + /** + * + * + * @param transport + */ + public void onSocketTimeout( + TransportProtocol transport /*, + boolean stillConnected*/); + + /** + * + * + * @param transport + */ + public void onDisconnect(TransportProtocol transport); + + /** + * + * + * @param transport + */ + public void onConnected(TransportProtocol transport); +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolException.java b/src/com/sshtools/j2ssh/transport/TransportProtocolException.java new file mode 100644 index 0000000000000000000000000000000000000000..5e886bdb62906bc37db7648ae2e5013ff4bc84d6 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolException.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public class TransportProtocolException extends SshException { + /** + * Creates a new TransportProtocolException object. + * + * @param msg + */ + public TransportProtocolException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolInputStream.java b/src/com/sshtools/j2ssh/transport/TransportProtocolInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..94f532f7992d58d8d777a2f0e3e84671374ebcbd --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolInputStream.java @@ -0,0 +1,335 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.cipher.SshCipher; +import com.sshtools.j2ssh.transport.compression.SshCompression; +import com.sshtools.j2ssh.transport.hmac.SshHmac; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; + +import java.math.BigInteger; + +import java.net.SocketException; + +import java.util.Iterator; + + +class TransportProtocolInputStream { + private static Log log = LogFactory.getLog(TransportProtocolInputStream.class); + private long bytesTransfered = 0; + private BufferedInputStream in; + private Object sequenceLock = new Object(); + private TransportProtocolCommon transport; + private TransportProtocolAlgorithmSync algorithms; + private long sequenceNo = 0; + private long sequenceWrapLimit = BigInteger.valueOf(2).pow(32).longValue(); + private SshCipher cipher; + private SshHmac hmac; + private SshCompression compression; + int msglen; + int padlen; + int read; + int remaining; + int cipherlen = 8; + int maclen = 0; + + //byte[] buffer = new byte[128 * cipherlen]; + ByteArrayWriter message = new ByteArrayWriter(); + byte[] initial = new byte[cipherlen]; + byte[] data = new byte[65535]; + byte[] buffered = new byte[65535]; + int startpos = 0; + int endpos = 0; + + /** + * Creates a new TransportProtocolInputStream object. + * + * @param transport + * @param in + * @param algorithms + * + * @throws IOException + */ + public TransportProtocolInputStream(TransportProtocolCommon transport, + + /*Socket socket,*/ + InputStream in, TransportProtocolAlgorithmSync algorithms) + throws IOException { + this.transport = transport; + + this.in = new BufferedInputStream(in); //socket.getInputStream()); + + this.algorithms = algorithms; + } + + /** + * + * + * @return + */ + public synchronized long getSequenceNo() { + return sequenceNo; + } + + /** + * + * + * @return + */ + protected long getNumBytesTransfered() { + return bytesTransfered; + } + + /** + * + * + * @return + */ + protected int available() { + return endpos - startpos; + } + + /** + * + * + * @param buf + * @param off + * @param len + * + * @return + * + * @throws IOException + */ + protected int readBufferedData(byte[] buf, int off, int len) + throws IOException { + int read; + + if ((endpos - startpos) < len) { + // Double check the buffer has enough room for the data + if ((buffered.length - endpos) < len) { + /*if (log.isDebugEnabled()) { + log.debug("Trimming used data from buffer"); + }*/ + + // no it does not odds are that the startpos is too high + System.arraycopy(buffered, startpos, buffered, 0, + endpos - startpos); + + endpos -= startpos; + + startpos = 0; + + if ((buffered.length - endpos) < len) { + //log.debug("Resizing message buffer"); + // Last resort resize the buffer to the required length + // this should stop any chance of error + byte[] tmp = new byte[buffered.length + len]; + + System.arraycopy(buffered, 0, tmp, 0, endpos); + + buffered = tmp; + } + } + + // If there is not enough data then block and read until there is (if still connected) + while (((endpos - startpos) < len) && + (transport.getState().getValue() != TransportProtocolState.DISCONNECTED)) { + try { + read = in.read(buffered, endpos, (buffered.length - endpos)); + } catch (InterruptedIOException ex) { + // We have an interrupted io; inform the event handler + read = ex.bytesTransferred; + + Iterator it = transport.getEventHandlers().iterator(); + + TransportProtocolEventHandler eventHandler; + + while (it.hasNext()) { + eventHandler = (TransportProtocolEventHandler) it.next(); + + eventHandler.onSocketTimeout(transport); + } + } + + if (read < 0) { + throw new IOException("The socket is EOF"); + } + + endpos += read; + } + } + + try { + System.arraycopy(buffered, startpos, buf, off, len); + } catch (Throwable t) { + System.out.println(); + } + + startpos += len; + + /*if (log.isDebugEnabled()) { + log.debug("Buffer StartPos=" + String.valueOf(startpos) + + " EndPos=" + String.valueOf(endpos)); + }*/ + + // Try to reset the buffer + if (startpos >= endpos) { + //if (log.isDebugEnabled()) { + // log.debug("Buffer has been reset"); + // }*/ + endpos = 0; + + startpos = 0; + } + + return len; + } + + /** + * + * + * @return + * + * @throws SocketException + * @throws IOException + */ + public byte[] readMessage() throws SocketException, IOException { + // Reset the message for the next + message.reset(); + + // Read the first byte of this message (this is so we block + // but we will determine the cipher length before reading all + read = readBufferedData(initial, 0, cipherlen); + + cipher = algorithms.getCipher(); + + hmac = algorithms.getHmac(); + + compression = algorithms.getCompression(); + + // If the cipher object has been set then make sure + // we have the correct blocksize + if (cipher != null) { + cipherlen = cipher.getBlockSize(); + } else { + cipherlen = 8; + } + + // Verify we have enough buffer size for the inital block + if (initial.length != cipherlen) { + // Create a temporary array for the new block size and copy + byte[] tmp = new byte[cipherlen]; + + System.arraycopy(initial, 0, tmp, 0, initial.length); + + // Now change the initial buffer to our new array + initial = tmp; + } + + // Now read the rest of the first block of data if necersary + int count = read; + + if (count < initial.length) { + count += readBufferedData(initial, count, initial.length - count); + } + + // Record the mac length + if (hmac != null) { + maclen = hmac.getMacLength(); + } else { + maclen = 0; + } + + // Decrypt the data if we have a valid cipher + if (cipher != null) { + initial = cipher.transform(initial); + } + + // Save the initial data + message.write(initial); + + // Preview the message length + msglen = (int) ByteArrayReader.readInt(initial, 0); + + padlen = initial[4]; + + // Read, decrypt and save the remaining data + remaining = (msglen - (cipherlen - 4)); + + while (remaining > 0) { + read = readBufferedData(data, 0, + (remaining < data.length) + ? ((remaining / cipherlen) * cipherlen) + : ((data.length / cipherlen) * cipherlen)); + remaining -= read; + + // Decrypt the data and/or write it to the message + message.write((cipher == null) ? data + : cipher.transform(data, 0, read), + 0, read); + } + + synchronized (sequenceLock) { + if (hmac != null) { + read = readBufferedData(data, 0, maclen); + + message.write(data, 0, read); + + // Verify the mac + if (!hmac.verify(sequenceNo, message.toByteArray())) { + throw new IOException("Corrupt Mac on input"); + } + } + + // Increment the sequence no + if (sequenceNo < sequenceWrapLimit) { + sequenceNo++; + } else { + sequenceNo = 0; + } + } + + bytesTransfered += message.size(); + + byte[] msg = message.toByteArray(); + + // Uncompress the message payload if necersary + if (compression != null) { + return compression.uncompress(msg, 5, (msglen + 4) - padlen - 5); + } + + return msg; + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolOutputStream.java b/src/com/sshtools/j2ssh/transport/TransportProtocolOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..c4875d931a2bea88e1bffad7d1d03d1dfb0f52a7 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolOutputStream.java @@ -0,0 +1,197 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.cipher.SshCipher; +import com.sshtools.j2ssh.transport.compression.SshCompression; +import com.sshtools.j2ssh.transport.hmac.SshHmac; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.OutputStream; + +import java.math.BigInteger; + +import java.util.Random; + + +class TransportProtocolOutputStream { + //implements Runnable { + private static Log log = LogFactory.getLog(TransportProtocolOutputStream.class); + private OutputStream out; + + //private Socket socket; + private TransportProtocolAlgorithmSync algorithms; + private TransportProtocolCommon transport; + private long sequenceNo = 0; + private long sequenceWrapLimit = BigInteger.valueOf(2).pow(32).longValue(); + private Random rnd = ConfigurationLoader.getRND(); + private long bytesTransfered = 0; + + /** + * Creates a new TransportProtocolOutputStream object. + * + * @param out + * @param transport + * @param algorithms + * + * @throws TransportProtocolException + */ + public TransportProtocolOutputStream( /*Socket socket,*/ + OutputStream out, TransportProtocolCommon transport, + TransportProtocolAlgorithmSync algorithms) + throws TransportProtocolException { + // try { + //this.socket = socket; + this.out = out; //socket.getOutputStream(); + this.transport = transport; + this.algorithms = algorithms; + + /* } catch (IOException ioe) { + throw new TransportProtocolException( + "Failed to obtain socket output stream"); + }*/ + } + + /** + * + * + * @return + */ + protected long getNumBytesTransfered() { + return bytesTransfered; + } + + /** + * + * + * @param msg + * + * @throws TransportProtocolException + */ + protected synchronized void sendMessage(SshMessage msg) + throws TransportProtocolException { + try { + // Get the algorithm objects + algorithms.lock(); + + SshCipher cipher = algorithms.getCipher(); + SshHmac hmac = algorithms.getHmac(); + SshCompression compression = algorithms.getCompression(); + + // Write the data into a byte array + ByteArrayWriter message = new ByteArrayWriter(); + + // Get the message payload data + byte[] msgdata = msg.toByteArray(); + + //int payload = msgdata.length; + int padding = 4; + int cipherlen = 8; + + // Determine the cipher length + if (cipher != null) { + cipherlen = cipher.getBlockSize(); + } + + // Compress the payload if necersary + if (compression != null) { + msgdata = compression.compress(msgdata, 0, msgdata.length); + } + + //Determine the padding length + padding += ((cipherlen - + ((msgdata.length + 5 + padding) % cipherlen)) % cipherlen); + + // Write the packet length field + message.writeInt(msgdata.length + 1 + padding); + + // Write the padding length + message.write(padding); + + // Write the message payload + message.write(msgdata, 0, msgdata.length); + + // Create some random data for the padding + byte[] pad = new byte[padding]; + rnd.nextBytes(pad); + + // Write the padding + message.write(pad); + + // Get the unencrypted packet data + byte[] packet = message.toByteArray(); + byte[] mac = null; + + // Generate the MAC + if (hmac != null) { + mac = hmac.generate(sequenceNo, packet, 0, packet.length); + } + + // Perfrom encrpytion + if (cipher != null) { + packet = cipher.transform(packet); + } + + // Reset the message + message.reset(); + + // Write the packet data + message.write(packet); + + // Combine the packet and MAC + if (mac != null) { + message.write(mac); + } + + bytesTransfered += message.size(); + + // Send! + out.write(message.toByteArray()); + + out.flush(); + + // Increment the sequence no + if (sequenceNo < sequenceWrapLimit) { + sequenceNo++; + } else { + sequenceNo = 0; + } + } catch (IOException ioe) { + if (transport.getState().getValue() != TransportProtocolState.DISCONNECTED) { + throw new TransportProtocolException("IO Error on socket: " + + ioe.getMessage()); + } + } + finally { + algorithms.release(); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/TransportProtocolState.java b/src/com/sshtools/j2ssh/transport/TransportProtocolState.java new file mode 100644 index 0000000000000000000000000000000000000000..975faba7fb8b3092934a79c9275390180c7380a5 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/TransportProtocolState.java @@ -0,0 +1,125 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport; + +import com.sshtools.j2ssh.util.State; + +import java.io.IOException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.24 $ + */ +public class TransportProtocolState extends State { + /** */ + public final static int UNINITIALIZED = 1; + + /** */ + public final static int NEGOTIATING_PROTOCOL = 2; + + /** */ + public final static int PERFORMING_KEYEXCHANGE = 3; + + /** */ + public final static int CONNECTED = 4; + + /** */ + public final static int DISCONNECTED = 5; + + /** */ + public IOException lastError; + + /** */ + public String reason = ""; + + /** + * Creates a new TransportProtocolState object. + */ + public TransportProtocolState() { + super(UNINITIALIZED); + } + + /** + * + * + * @param lastError + */ + protected void setLastError(IOException lastError) { + this.lastError = lastError; + } + + /** + * + * + * @return + */ + public boolean hasError() { + return lastError != null; + } + + /** + * + * + * @return + */ + public IOException getLastError() { + return lastError; + } + + /** + * + * + * @param reason + */ + protected void setDisconnectReason(String reason) { + this.reason = reason; + } + + /** + * + * + * @return + */ + public String getDisconnectReason() { + return reason; + } + + /** + * + * + * @param state + * + * @return + */ + public boolean isValidState(int state) { + return ((state == UNINITIALIZED) || (state == NEGOTIATING_PROTOCOL) || + (state == PERFORMING_KEYEXCHANGE) || (state == CONNECTED) || + (state == DISCONNECTED)); + } +} diff --git a/src/com/sshtools/j2ssh/transport/cipher/BlowfishCbc.java b/src/com/sshtools/j2ssh/transport/cipher/BlowfishCbc.java new file mode 100644 index 0000000000000000000000000000000000000000..44c3375e2b372082412f9621eeced5294ff6f3a0 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/cipher/BlowfishCbc.java @@ -0,0 +1,128 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.cipher; + +import com.sshtools.j2ssh.transport.AlgorithmOperationException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class BlowfishCbc extends SshCipher { + private static Log log = LogFactory.getLog(BlowfishCbc.class); + + /** */ + protected static String algorithmName = "blowfish-cbc"; + Cipher cipher; + + /** + * Creates a new BlowfishCbc object. + */ + public BlowfishCbc() { + } + + /** + * + * + * @return + */ + public int getBlockSize() { + return cipher.getBlockSize(); + } + + /** + * + * + * @param mode + * @param iv + * @param keydata + * + * @throws AlgorithmOperationException + */ + public void init(int mode, byte[] iv, byte[] keydata) + throws AlgorithmOperationException { + try { + cipher = Cipher.getInstance("Blowfish/CBC/NoPadding"); + + // Create a 16 byte key + byte[] actualKey = new byte[16]; + System.arraycopy(keydata, 0, actualKey, 0, actualKey.length); + + SecretKeySpec keyspec = new SecretKeySpec(actualKey, "Blowfish"); + + // Create the cipher according to its algorithm + cipher.init(((mode == ENCRYPT_MODE) ? Cipher.ENCRYPT_MODE + : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv, 0, cipher.getBlockSize())); + } catch (NoSuchPaddingException nspe) { + log.error("Blowfish initialization failed", nspe); + throw new AlgorithmOperationException("No Padding not supported"); + } catch (NoSuchAlgorithmException nsae) { + log.error("Blowfish initialization failed", nsae); + throw new AlgorithmOperationException("Algorithm not supported"); + } catch (InvalidKeyException ike) { + log.error("Blowfish initialization failed", ike); + throw new AlgorithmOperationException("Invalid encryption key"); + + /*} catch (InvalidKeySpecException ispe) { + throw new AlgorithmOperationException("Invalid encryption key specification");*/ + } catch (InvalidAlgorithmParameterException ape) { + log.error("Blowfish initialization failed", ape); + throw new AlgorithmOperationException("Invalid algorithm parameter"); + } + } + + /** + * + * + * @param data + * @param offset + * @param len + * + * @return + * + * @throws AlgorithmOperationException + */ + public byte[] transform(byte[] data, int offset, int len) + throws AlgorithmOperationException { + return cipher.update(data, offset, len); + } +} diff --git a/src/com/sshtools/j2ssh/transport/cipher/SshCipher.java b/src/com/sshtools/j2ssh/transport/cipher/SshCipher.java new file mode 100644 index 0000000000000000000000000000000000000000..0fea0aa61fc5b139cae08f320290ac09d8bdf43a --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/cipher/SshCipher.java @@ -0,0 +1,89 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.cipher; + +import com.sshtools.j2ssh.transport.AlgorithmOperationException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public abstract class SshCipher { + /** */ + public static final int ENCRYPT_MODE = 0; + + /** */ + public static final int DECRYPT_MODE = 1; + + /** + * + * + * @return + */ + public abstract int getBlockSize(); + + /** + * + * + * @param mode + * @param iv + * @param keydata + * + * @throws AlgorithmOperationException + */ + public abstract void init(int mode, byte[] iv, byte[] keydata) + throws AlgorithmOperationException; + + /** + * + * + * @param data + * + * @return + * + * @throws AlgorithmOperationException + */ + public byte[] transform(byte[] data) throws AlgorithmOperationException { + return transform(data, 0, data.length); + } + + /** + * + * + * @param data + * @param offset + * @param len + * + * @return + * + * @throws AlgorithmOperationException + */ + public abstract byte[] transform(byte[] data, int offset, int len) + throws AlgorithmOperationException; +} diff --git a/src/com/sshtools/j2ssh/transport/cipher/SshCipherFactory.java b/src/com/sshtools/j2ssh/transport/cipher/SshCipherFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..01db3392eb844af3cef7aa3b7f416d8cf72ce285 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/cipher/SshCipherFactory.java @@ -0,0 +1,165 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.cipher; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.IOUtil; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.InputStream; + +import java.net.URL; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.32 $ + */ +public class SshCipherFactory { + private static HashMap ciphers; + private static String defaultCipher; + private static Log log = LogFactory.getLog(SshCipherFactory.class); + private static ArrayList supported; + + static { + ciphers = new HashMap(); + + log.info("Loading supported cipher algorithms"); + + ciphers.put("3des-cbc", TripleDesCbc.class); + ciphers.put("blowfish-cbc", BlowfishCbc.class); + defaultCipher = "blowfish-cbc"; + + try { + Enumeration en = ConfigurationLoader.getExtensionClassLoader() + .getResources("j2ssh.cipher"); + URL url; + Properties properties = new Properties(); + InputStream in; + + while ((en != null) && en.hasMoreElements()) { + url = (URL) en.nextElement(); + in = url.openStream(); + properties.load(in); + IOUtil.closeStream(in); + + int num = 1; + String name = ""; + Class cls; + + while (properties.getProperty("cipher.name." + + String.valueOf(num)) != null) { + try { + name = properties.getProperty("cipher.name." + + String.valueOf(num)); + cls = ConfigurationLoader.getExtensionClassLoader() + .loadClass(properties.getProperty( + "cipher.class." + String.valueOf(num))); + cls.newInstance(); + ciphers.put(name, cls); + log.info("Installed " + name + " cipher"); + } catch (Throwable ex) { + log.info("Could not install cipher class for " + name, + ex); + } + + num++; + } + } + } catch (Throwable t) { + } + + // Build a list of the supported ciphers + supported = new ArrayList(ciphers.keySet()); + } + + /** + * Creates a new SshCipherFactory object. + */ + protected SshCipherFactory() { + } + + /** + * + */ + public static void initialize() { + } + + /** + * + * + * @return + */ + public static String getDefaultCipher() { + return defaultCipher; + } + + /** + * + * + * @return + */ + public static List getSupportedCiphers() { + // Return the list + return supported; + } + + /** + * + * + * @param algorithmName + * + * @return + * + * @throws AlgorithmNotSupportedException + */ + public static SshCipher newInstance(String algorithmName) + throws AlgorithmNotSupportedException { + log.info("Creating new " + algorithmName + " cipher instance"); + + try { + return (SshCipher) ((Class) ciphers.get(algorithmName)).newInstance(); + } catch (Throwable t) { + throw new AlgorithmNotSupportedException(algorithmName + + " is not supported!"); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/cipher/TripleDesCbc.java b/src/com/sshtools/j2ssh/transport/cipher/TripleDesCbc.java new file mode 100644 index 0000000000000000000000000000000000000000..778510bc035864b32db8e2639d88a45755a88fe4 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/cipher/TripleDesCbc.java @@ -0,0 +1,124 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.cipher; + +import com.sshtools.j2ssh.transport.AlgorithmOperationException; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class TripleDesCbc extends SshCipher { + /** */ + protected static String algorithmName = "3des-cbc"; + Cipher cipher; + + /** + * Creates a new TripleDesCbc object. + */ + public TripleDesCbc() { + } + + /** + * + * + * @return + */ + public int getBlockSize() { + return cipher.getBlockSize(); + } + + /** + * + * + * @param mode + * @param iv + * @param keydata + * + * @throws AlgorithmOperationException + */ + public void init(int mode, byte[] iv, byte[] keydata) + throws AlgorithmOperationException { + try { + KeySpec keyspec; + Key key; + + // Create the cipher according to its algorithm + cipher = Cipher.getInstance("DESede/CBC/NoPadding"); + + byte[] actualKey = new byte[24]; + System.arraycopy(keydata, 0, actualKey, 0, 24); + keyspec = new DESedeKeySpec(actualKey); + key = SecretKeyFactory.getInstance("DESede").generateSecret(keyspec); + cipher.init(((mode == ENCRYPT_MODE) ? Cipher.ENCRYPT_MODE + : Cipher.DECRYPT_MODE), key, + new IvParameterSpec(iv, 0, cipher.getBlockSize())); + } catch (NoSuchPaddingException nspe) { + throw new AlgorithmOperationException("Padding not supported"); + } catch (NoSuchAlgorithmException nsae) { + throw new AlgorithmOperationException("Algorithm not supported"); + } catch (InvalidKeyException ike) { + throw new AlgorithmOperationException("Invalid encryption key"); + } catch (InvalidKeySpecException ispe) { + throw new AlgorithmOperationException( + "Invalid encryption key specification"); + } catch (InvalidAlgorithmParameterException ape) { + throw new AlgorithmOperationException("Invalid algorithm parameter"); + } + } + + /** + * + * + * @param data + * @param offset + * @param len + * + * @return + * + * @throws AlgorithmOperationException + */ + public byte[] transform(byte[] data, int offset, int len) + throws AlgorithmOperationException { + return cipher.update(data, offset, len); + } +} diff --git a/src/com/sshtools/j2ssh/transport/compression/SshCompression.java b/src/com/sshtools/j2ssh/transport/compression/SshCompression.java new file mode 100644 index 0000000000000000000000000000000000000000..1ba18de9f6d2a5ada2681fa4f86d9e85df6e1686 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/compression/SshCompression.java @@ -0,0 +1,58 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.compression; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public interface SshCompression { + static public final int INFLATER = 0; + static public final int DEFLATER = 1; + + public void init(int type, int level); + + /** + * + * + * @param data + * + * @return + */ + public byte[] compress(byte[] data, int start, int len); + + /** + * + * + * @param data + * + * @return + */ + public byte[] uncompress(byte[] data, int start, int len); +} diff --git a/src/com/sshtools/j2ssh/transport/compression/SshCompressionFactory.java b/src/com/sshtools/j2ssh/transport/compression/SshCompressionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..386fc3e42ccfafababb6388f4d6dc6485d042cb9 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/compression/SshCompressionFactory.java @@ -0,0 +1,164 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.compression; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.IOUtil; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.InputStream; + +import java.net.URL; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.30 $ + */ +public class SshCompressionFactory { + /** */ + public final static String COMP_NONE = "none"; + private static String defaultAlgorithm; + private static Map comps; + private static Log log = LogFactory.getLog(SshCompressionFactory.class); + + static { + comps = new HashMap(); + + log.info("Loading compression methods"); + + comps.put(COMP_NONE, ""); + + defaultAlgorithm = COMP_NONE; + + try { + Enumeration en = ConfigurationLoader.getExtensionClassLoader() + .getResources("j2ssh.compression"); + URL url; + Properties properties = new Properties(); + InputStream in; + + while ((en != null) && en.hasMoreElements()) { + url = (URL) en.nextElement(); + in = url.openStream(); + properties.load(in); + IOUtil.closeStream(in); + + int num = 1; + String name = ""; + Class cls; + + while (properties.getProperty("compression.name." + + String.valueOf(num)) != null) { + try { + name = properties.getProperty("compression.name." + + String.valueOf(num)); + cls = ConfigurationLoader.getExtensionClassLoader() + .loadClass(properties.getProperty( + "compression.class." + String.valueOf(num))); + cls.newInstance(); + comps.put(name, cls); + log.info("Installed " + name + " compression"); + } catch (Throwable ex) { + log.info("Could not install cipher class for " + name, + ex); + } + + num++; + } + } + } catch (Throwable t) { + } + } + + /** + * Creates a new SshCompressionFactory object. + */ + protected SshCompressionFactory() { + } + + /** + * + */ + public static void initialize() { + } + + /** + * + * + * @return + */ + public static String getDefaultCompression() { + return defaultAlgorithm; + } + + /** + * + * + * @return + */ + public static List getSupportedCompression() { + return new ArrayList(comps.keySet()); + } + + /** + * + * + * @param algorithmName + * + * @return + * + * @throws AlgorithmNotSupportedException + */ + public static SshCompression newInstance(String algorithmName) + throws AlgorithmNotSupportedException { + try { + if (algorithmName.equals(COMP_NONE)) { + return null; + } else { + return (SshCompression) ((Class) comps.get(algorithmName)).newInstance(); + } + } catch (Exception e) { + throw new AlgorithmNotSupportedException(algorithmName + + " is not supported!"); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/hmac/HmacMd5.java b/src/com/sshtools/j2ssh/transport/hmac/HmacMd5.java new file mode 100644 index 0000000000000000000000000000000000000000..d9a4211b63b3d563ef13e00a1b0270da98338c59 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/hmac/HmacMd5.java @@ -0,0 +1,125 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.hmac; + +import com.sshtools.j2ssh.transport.AlgorithmInitializationException; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class HmacMd5 implements SshHmac { + private Mac mac; + + /** + * Creates a new HmacMd5 object. + */ + public HmacMd5() { + } + + /** + * + * + * @return + */ + public int getMacLength() { + return mac.getMacLength(); + } + + /** + * + * + * @param sequenceNo + * @param data + * @param offset + * @param len + * + * @return + */ + public byte[] generate(long sequenceNo, byte[] data, int offset, int len) { + // Write the sequence no + byte[] sequenceBytes = new byte[4]; + sequenceBytes[0] = (byte) (sequenceNo >> 24); + sequenceBytes[1] = (byte) (sequenceNo >> 16); + sequenceBytes[2] = (byte) (sequenceNo >> 8); + sequenceBytes[3] = (byte) (sequenceNo >> 0); + mac.update(sequenceBytes); + mac.update(data, offset, len); + + return mac.doFinal(); + } + + /** + * + * + * @param keydata + * + * @throws AlgorithmInitializationException + */ + public void init(byte[] keydata) throws AlgorithmInitializationException { + try { + mac = Mac.getInstance("HmacMD5"); + + // Create a key of 16 bytes + byte[] key = new byte[16]; + System.arraycopy(keydata, 0, key, 0, key.length); + + SecretKeySpec keyspec = new SecretKeySpec(key, "HmacMD5"); + mac.init(keyspec); + } catch (NoSuchAlgorithmException nsae) { + throw new AlgorithmInitializationException( + "No provider exists for the HmacSha1 algorithm"); + } catch (InvalidKeyException ike) { + throw new AlgorithmInitializationException("Invalid key"); + } + } + + /** + * + * + * @param sequenceNo + * @param data + * + * @return + */ + public boolean verify(long sequenceNo, byte[] data) { + int len = getMacLength(); + byte[] generated = generate(sequenceNo, data, 0, data.length - len); + String compare1 = new String(generated); + String compare2 = new String(data, data.length - len, len); + + return compare1.equals(compare2); + } +} diff --git a/src/com/sshtools/j2ssh/transport/hmac/HmacMd596.java b/src/com/sshtools/j2ssh/transport/hmac/HmacMd596.java new file mode 100644 index 0000000000000000000000000000000000000000..7405721e831a60dcf84b787308dae520cb240fb2 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/hmac/HmacMd596.java @@ -0,0 +1,68 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.hmac; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class HmacMd596 extends HmacMd5 { + /** + * Creates a new HmacMd596 object. + */ + public HmacMd596() { + } + + /** + * + * + * @return + */ + public int getMacLength() { + return 12; + } + + /** + * + * + * @param sequenceNo + * @param data + * @param offset + * @param len + * + * @return + */ + public byte[] generate(long sequenceNo, byte[] data, int offset, int len) { + byte[] generated = super.generate(sequenceNo, data, offset, len); + byte[] result = new byte[getMacLength()]; + System.arraycopy(generated, 0, result, 0, getMacLength()); + + return result; + } +} diff --git a/src/com/sshtools/j2ssh/transport/hmac/HmacSha.java b/src/com/sshtools/j2ssh/transport/hmac/HmacSha.java new file mode 100644 index 0000000000000000000000000000000000000000..302e228c059c4201fa312e3983a6b4a6e0c2b94d --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/hmac/HmacSha.java @@ -0,0 +1,150 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.hmac; + +import com.sshtools.j2ssh.transport.AlgorithmInitializationException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class HmacSha implements SshHmac { + private static Log log = LogFactory.getLog(HmacSha.class); + private Mac mac; + + /** + * Creates a new HmacSha object. + */ + public HmacSha() { + } + + /** + * + * + * @return + */ + public int getMacLength() { + return mac.getMacLength(); + } + + /** + * + * + * @param sequenceNo + * @param data + * @param offset + * @param len + * + * @return + */ + public byte[] generate(long sequenceNo, byte[] data, int offset, int len) { + // Write the sequence no + byte[] sequenceBytes = new byte[4]; + sequenceBytes[0] = (byte) (sequenceNo >> 24); + sequenceBytes[1] = (byte) (sequenceNo >> 16); + sequenceBytes[2] = (byte) (sequenceNo >> 8); + sequenceBytes[3] = (byte) (sequenceNo >> 0); + mac.update(sequenceBytes); + mac.update(data, offset, len); + + return mac.doFinal(); + } + + /** + * + * + * @param keydata + * + * @throws AlgorithmInitializationException + */ + public void init(byte[] keydata) throws AlgorithmInitializationException { + try { + mac = Mac.getInstance("HmacSha1"); + + byte[] key = new byte[20]; + System.arraycopy(keydata, 0, key, 0, 20); + + SecretKeySpec keyspec = new SecretKeySpec(key, "HmacSha1"); + mac.init(keyspec); + } catch (NoSuchAlgorithmException nsae) { + throw new AlgorithmInitializationException( + "No provider exists for the HmacSha1 algorithm"); + } catch (InvalidKeyException ike) { + throw new AlgorithmInitializationException("Invalid key"); + } + } + + /** + * + * + * @param sequenceNo + * @param data + * + * @return + */ + public boolean verify(long sequenceNo, byte[] data) { + int len = getMacLength(); + + //log.debug("MAC Data length: " + String.valueOf(data.length)); + byte[] generated = generate(sequenceNo, data, 0, data.length - len); + String compare1 = new String(generated); + String compare2 = new String(data, data.length - len, len); + + //log.debug("Generated: " + compare1); + //log.debug("Actual : " + compare2); + boolean result = compare1.equals(compare2); + + /*if (!result) { + /** + * Output some debug stuff + */ + /* String genhex = ""; + String acthex = ""; + boolean verify = true; + for(int i=0;i<generated.length;i++) { + genhex += (genhex.length()==0?"":",") + Integer.toHexString(generated[i] & 0xFF); + acthex += (acthex.length()==0?"":",") + Integer.toHexString(data[data.length-len+i] & 0xFF); + verify = (generated[i] == data[data.length-len+i]); + } + log.debug("Byte Verify: " + String.valueOf(verify)); + log.debug("Generated: " + genhex); + log.debug("Actual: " + acthex); + }*/ + return result; + } +} diff --git a/src/com/sshtools/j2ssh/transport/hmac/HmacSha96.java b/src/com/sshtools/j2ssh/transport/hmac/HmacSha96.java new file mode 100644 index 0000000000000000000000000000000000000000..ec113be836d6228c89c0698da557840666adbe30 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/hmac/HmacSha96.java @@ -0,0 +1,68 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.hmac; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class HmacSha96 extends HmacSha { + /** + * Creates a new HmacSha96 object. + */ + public HmacSha96() { + } + + /** + * + * + * @return + */ + public int getMacLength() { + return 12; + } + + /** + * + * + * @param sequenceNo + * @param data + * @param offset + * @param len + * + * @return + */ + public byte[] generate(long sequenceNo, byte[] data, int offset, int len) { + byte[] generated = super.generate(sequenceNo, data, offset, len); + byte[] result = new byte[getMacLength()]; + System.arraycopy(generated, 0, result, 0, getMacLength()); + + return result; + } +} diff --git a/src/com/sshtools/j2ssh/transport/hmac/SshHmac.java b/src/com/sshtools/j2ssh/transport/hmac/SshHmac.java new file mode 100644 index 0000000000000000000000000000000000000000..73b42be32f589053fbd7c9049273a41ebbce4b2b --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/hmac/SshHmac.java @@ -0,0 +1,75 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.hmac; + +import com.sshtools.j2ssh.transport.AlgorithmInitializationException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public interface SshHmac { + /** + * + * + * @return + */ + public int getMacLength(); + + /** + * + * + * @param sequenceNo + * @param data + * @param offset + * @param len + * + * @return + */ + public byte[] generate(long sequenceNo, byte[] data, int offset, int len); + + /** + * + * + * @param keydata + * + * @throws AlgorithmInitializationException + */ + public void init(byte[] keydata) throws AlgorithmInitializationException; + + /** + * + * + * @param sequenceNo + * @param data + * + * @return + */ + public boolean verify(long sequenceNo, byte[] data); +} diff --git a/src/com/sshtools/j2ssh/transport/hmac/SshHmacFactory.java b/src/com/sshtools/j2ssh/transport/hmac/SshHmacFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..304fcb73cf9add025997a9a2372631ecf3d61a1b --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/hmac/SshHmacFactory.java @@ -0,0 +1,114 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.hmac; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.30 $ + */ +public class SshHmacFactory { + private static String defaultAlgorithm; + private static Map macs; + private static Log log = LogFactory.getLog(SshHmacFactory.class); + + static { + macs = new HashMap(); + + log.info("Loading message authentication methods"); + + macs.put("hmac-sha1", HmacSha.class); + macs.put("hmac-sha1-96", HmacSha96.class); + macs.put("hmac-md5", HmacMd5.class); + macs.put("hmac-md5-96", HmacMd596.class); + + defaultAlgorithm = "hmac-sha1"; + } + + /** + * Creates a new SshHmacFactory object. + */ + protected SshHmacFactory() { + } + + /** + * + */ + public static void initialize() { + } + + /** + * + * + * @return + */ + public final static String getDefaultHmac() { + return defaultAlgorithm; + } + + /** + * + * + * @return + */ + public static List getSupportedMacs() { + return new ArrayList(macs.keySet()); + } + + /** + * + * + * @param methodName + * + * @return + * + * @throws AlgorithmNotSupportedException + */ + public static SshHmac newInstance(String methodName) + throws AlgorithmNotSupportedException { + try { + return (SshHmac) ((Class) macs.get(methodName)).newInstance(); + } catch (Exception e) { + throw new AlgorithmNotSupportedException(methodName + + " is not supported!"); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/kex/DhGroup1Sha1.java b/src/com/sshtools/j2ssh/transport/kex/DhGroup1Sha1.java new file mode 100644 index 0000000000000000000000000000000000000000..913f61dc76b3c200bc85d1db0df67a63a0378d78 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/kex/DhGroup1Sha1.java @@ -0,0 +1,313 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.kex; + +import com.sshtools.j2ssh.SshException; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; +import com.sshtools.j2ssh.transport.AlgorithmOperationException; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.util.Hash; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.KeyAgreement; +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.25 $ + */ +public class DhGroup1Sha1 extends SshKeyExchange { + private static Log log = LogFactory.getLog(DhGroup1Sha1.class); + private static BigInteger g = new BigInteger("2"); + private static BigInteger p = new BigInteger(new byte[] { + (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, + (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21, (byte) 0x68, + (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62, + (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1, + (byte) 0x29, (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A, + (byte) 0x67, (byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B, + (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B, + (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, + (byte) 0x8E, (byte) 0x34, (byte) 0x04, (byte) 0xDD, (byte) 0xEF, + (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, (byte) 0x3A, + (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, + (byte) 0x6D, (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37, + (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D, + (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, + (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E, + (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, (byte) 0xE9, + (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, (byte) 0x0B, + (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06, + (byte) 0xB7, (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, + (byte) 0xFB, (byte) 0x5A, (byte) 0x89, (byte) 0x9F, (byte) 0xA5, + (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, + (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, + (byte) 0x66, (byte) 0x51, (byte) 0xEC, (byte) 0xE6, (byte) 0x53, + (byte) 0x81, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }); + private BigInteger e = null; + private BigInteger f = null; + + //private static BigInteger q = p.subtract(BigInteger.ONE).divide(g); + private BigInteger x = null; + private BigInteger y = null; + private String clientId; + private String serverId; + private byte[] clientKexInit; + private byte[] serverKexInit; + private KeyPairGenerator dhKeyPairGen; + private KeyAgreement dhKeyAgreement; + + /** + * Creates a new DhGroup1Sha1 object. + */ + public DhGroup1Sha1() { + } + + /** + * + * + * @throws IOException + * @throws AlgorithmNotSupportedException + */ + protected void onInit() throws IOException { + messageStore.registerMessage(SshMsgKexDhInit.SSH_MSG_KEXDH_INIT, + SshMsgKexDhInit.class); + messageStore.registerMessage(SshMsgKexDhReply.SSH_MSG_KEXDH_REPLY, + SshMsgKexDhReply.class); + + try { + dhKeyPairGen = KeyPairGenerator.getInstance("DH"); + dhKeyAgreement = KeyAgreement.getInstance("DH"); + } catch (NoSuchAlgorithmException ex) { + throw new AlgorithmNotSupportedException(ex.getMessage()); + } + } + + /** + * + * + * @param clientId + * @param serverId + * @param clientKexInit + * @param serverKexInit + * + * @throws IOException + * @throws AlgorithmOperationException + * @throws KeyExchangeException + */ + public void performClientExchange(String clientId, String serverId, + byte[] clientKexInit, byte[] serverKexInit) throws IOException { + log.info("Starting client side key exchange."); + this.clientId = clientId; + this.serverId = serverId; + this.clientKexInit = clientKexInit; + this.serverKexInit = serverKexInit; + + //int minBits = g.bitLength(); + //int maxBits = q.bitLength(); + //Random rnd = ConfigurationLoader.getRND(); + // Generate a random bit count for the random x value + + /*int genBits = (int) ( ( (maxBits - minBits + 1) * rnd.nextFloat()) + + minBits); + x = new BigInteger(genBits, rnd); + // Calculate e + e = g.modPow(x, p);*/ + try { + DHParameterSpec dhSkipParamSpec = new DHParameterSpec(p, g); + dhKeyPairGen.initialize(dhSkipParamSpec); + + KeyPair dhKeyPair = dhKeyPairGen.generateKeyPair(); + dhKeyAgreement.init(dhKeyPair.getPrivate()); + x = ((DHPrivateKey) dhKeyPair.getPrivate()).getX(); + e = ((DHPublicKey) dhKeyPair.getPublic()).getY(); + } catch (InvalidKeyException ex) { + throw new AlgorithmOperationException("Failed to generate DH value"); + } catch (InvalidAlgorithmParameterException ex) { + throw new AlgorithmOperationException("Failed to generate DH value"); + } + + // Prepare the message + SshMsgKexDhInit msg = new SshMsgKexDhInit(e); + + // Send it + try { + transport.sendMessage(msg, this); + } + catch (SshException tpe) { + throw new KeyExchangeException( + "Failed to send key exchange initialization message"); + } + + int[] messageId = new int[1]; + messageId[0] = SshMsgKexDhReply.SSH_MSG_KEXDH_REPLY; + + SshMsgKexDhReply reply = (SshMsgKexDhReply) transport.readMessage(messageId); + hostKey = reply.getHostKey(); + signature = reply.getSignature(); + f = reply.getF(); + + // Calculate diffe hellman k value + secret = f.modPow(x, p); + + // Calculate the exchange hash + calculateExchangeHash(); + } + + /** + * + * + * @param clientId + * @param serverId + * @param clientKexInit + * @param serverKexInit + * @param prvKey + * + * @throws IOException + * @throws KeyExchangeException + */ + public void performServerExchange(String clientId, String serverId, + byte[] clientKexInit, byte[] serverKexInit, SshPrivateKey prvKey) + throws IOException { + try { + this.clientId = clientId; + this.serverId = serverId; + this.clientKexInit = clientKexInit; + this.serverKexInit = serverKexInit; + + /*int minBits = g.bitLength(); + int maxBits = q.bitLength(); + Random rnd = ConfigurationLoader.getRND(); + // Generate a random bit count for the random x value + int genBits = (int) ( ( (maxBits - minBits + 1) * rnd.nextFloat()) + + minBits); + y = new BigInteger(genBits, rnd);*/ + try { + DHParameterSpec dhSkipParamSpec = new DHParameterSpec(p, g); + dhKeyPairGen.initialize(dhSkipParamSpec); + + KeyPair dhKeyPair = dhKeyPairGen.generateKeyPair(); + dhKeyAgreement.init(dhKeyPair.getPrivate()); + y = ((DHPrivateKey) dhKeyPair.getPrivate()).getX(); + f = ((DHPublicKey) dhKeyPair.getPublic()).getY(); + } catch (InvalidKeyException ex) { + throw new AlgorithmOperationException( + "Failed to generate DH y value"); + } catch (InvalidAlgorithmParameterException ex) { + throw new AlgorithmOperationException( + "Failed to generate DH y value"); + } + + // Calculate f + //f = g.modPow(y, p); + // Wait for the e value and calculate the other parameters + int[] messageId = new int[1]; + messageId[0] = SshMsgKexDhInit.SSH_MSG_KEXDH_INIT; + + SshMsgKexDhInit msg = (SshMsgKexDhInit) transport.readMessage(messageId); + e = msg.getE(); + + // Calculate k + secret = e.modPow(y, p); + hostKey = prvKey.getPublicKey().getEncoded(); + calculateExchangeHash(); + signature = prvKey.generateSignature(exchangeHash); + + SshMsgKexDhReply reply = new SshMsgKexDhReply(hostKey, f, signature); + transport.sendMessage(reply, this); + } catch (SshException e) { + throw new KeyExchangeException(e.getMessage()); + } + } + + /** + * + * + * @throws KeyExchangeException + */ + protected void calculateExchangeHash() throws KeyExchangeException { + Hash hash; + + try { + // Start a SHA hash + hash = new Hash("SHA"); + } catch (NoSuchAlgorithmException nsae) { + throw new KeyExchangeException("SHA algorithm not supported"); + } + + int i; + + // The local software version comments + hash.putString(clientId); + + // The remote software version comments + hash.putString(serverId); + + // The local kex init payload + hash.putInt(clientKexInit.length); + hash.putBytes(clientKexInit); + + // The remote kex init payload + hash.putInt(serverKexInit.length); + hash.putBytes(serverKexInit); + + // The host key + hash.putInt(hostKey.length); + hash.putBytes(hostKey); + + // The diffie hellman e value + hash.putBigInteger(e); + + // The diffie hellman f value + hash.putBigInteger(f); + + // The diffie hellman k value + hash.putBigInteger(secret); + + // Do the final output + exchangeHash = hash.doFinal(); + } +} diff --git a/src/com/sshtools/j2ssh/transport/kex/KeyExchangeException.java b/src/com/sshtools/j2ssh/transport/kex/KeyExchangeException.java new file mode 100644 index 0000000000000000000000000000000000000000..fd127a20f5680b18932f8f425a46a2b9f88fbd17 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/kex/KeyExchangeException.java @@ -0,0 +1,46 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.kex; + +import com.sshtools.j2ssh.transport.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class KeyExchangeException extends TransportProtocolException { + /** + * Creates a new KeyExchangeException object. + * + * @param msg + */ + public KeyExchangeException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/kex/KeyExchangeState.java b/src/com/sshtools/j2ssh/transport/kex/KeyExchangeState.java new file mode 100644 index 0000000000000000000000000000000000000000..dec4e8dce068f81009bdbb7420ea184aee4cea14 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/kex/KeyExchangeState.java @@ -0,0 +1,153 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.kex; + +import java.math.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class KeyExchangeState { + /** */ + public final static int IN_PROGRESS = 0; + + /** */ + public final static int COMPLETE = 1; + + /** */ + public final static int FAILED = 2; + private BigInteger secret; + private String reason; + private byte[] exchangeHash; + private byte[] hostKey; + private byte[] signature; + private int state = IN_PROGRESS; + + /** + * Creates a new KeyExchangeState object. + */ + public KeyExchangeState() { + } + + /** + * + * + * @param exchangeHash + * @param hostKey + * @param signature + * @param secret + */ + public final synchronized void setComplete(byte[] exchangeHash, + byte[] hostKey, byte[] signature, BigInteger secret) { + this.exchangeHash = exchangeHash; + this.hostKey = hostKey; + this.signature = signature; + this.secret = secret; + state = COMPLETE; + notifyAll(); + } + + /** + * + * + * @return + */ + public byte[] getExchangeHash() { + return exchangeHash; + } + + /** + * + * + * @param reason + */ + public final synchronized void setFailed(String reason) { + this.reason = reason; + state = FAILED; + notifyAll(); + } + + /** + * + * + * @return + */ + public byte[] getHostKey() { + return hostKey; + } + + /** + * + * + * @return + */ + public BigInteger getSecret() { + return secret; + } + + /** + * + * + * @return + */ + public byte[] getSignature() { + return signature; + } + + /** + * + * + * @return + */ + public synchronized int getState() { + return state; + } + + /** + * + */ + public final synchronized void waitForCompletion() { + while (state == IN_PROGRESS) { + try { + wait(); + } catch (InterruptedException e) { + } + } + } + + /** + * + * + * @return + */ + public synchronized String getFailureReason() { + return reason; + } +} diff --git a/src/com/sshtools/j2ssh/transport/kex/SshKeyExchange.java b/src/com/sshtools/j2ssh/transport/kex/SshKeyExchange.java new file mode 100644 index 0000000000000000000000000000000000000000..101a893eef99d79b844d7bd1aaf319285812713e --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/kex/SshKeyExchange.java @@ -0,0 +1,163 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.kex; + +import com.sshtools.j2ssh.transport.SshMessageStore; +import com.sshtools.j2ssh.transport.TransportProtocol; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; + +import java.io.IOException; + +import java.math.BigInteger; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public abstract class SshKeyExchange { //implements Runnable { + + /** */ + protected BigInteger secret; + + /** */ + protected SshMessageStore messageStore = new SshMessageStore(); + + /** */ + protected byte[] exchangeHash; + + /** */ + protected byte[] hostKey; + + /** */ + protected byte[] signature; + + /** */ + protected TransportProtocol transport; + + /** + * Creates a new SshKeyExchange object. + */ + public SshKeyExchange() { + } + + /** + * + * + * @return + */ + public byte[] getExchangeHash() { + return exchangeHash; + } + + /** + * + * + * @return + */ + public byte[] getHostKey() { + return hostKey; + } + + /** + * + * + * @return + */ + public BigInteger getSecret() { + return secret; + } + + /** + * + * + * @return + */ + public byte[] getSignature() { + return signature; + } + + /** + * + * + * @param transport + * + * @throws IOException + */ + public void init(TransportProtocol transport) throws IOException { + this.transport = transport; + onInit(); + transport.addMessageStore(messageStore); + } + + /** + * + * + * @throws IOException + */ + protected abstract void onInit() throws IOException; + + /** + * + * + * @param clientId + * @param serverId + * @param clientKexInit + * @param serverKexInit + * + * @throws IOException + */ + public abstract void performClientExchange(String clientId, + String serverId, byte[] clientKexInit, byte[] serverKexInit) + throws IOException; + + /** + * + * + * @param clientId + * @param serverId + * @param clientKexInit + * @param serverKexInit + * @param prvkey + * + * @throws IOException + */ + public abstract void performServerExchange(String clientId, + String serverId, byte[] clientKexInit, byte[] serverKexInit, + SshPrivateKey prvkey) throws IOException; + + /** + * + */ + public void reset() { + exchangeHash = null; + hostKey = null; + signature = null; + secret = null; + } +} diff --git a/src/com/sshtools/j2ssh/transport/kex/SshKeyExchangeFactory.java b/src/com/sshtools/j2ssh/transport/kex/SshKeyExchangeFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..df35c12d8d805a4ba8f172b6e4e80644ca7d2ea5 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/kex/SshKeyExchangeFactory.java @@ -0,0 +1,160 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.kex; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.ExtensionAlgorithm; +import com.sshtools.j2ssh.configuration.SshAPIConfiguration; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.28 $ + */ +public class SshKeyExchangeFactory { + private static Map kexs; + private static String defaultAlgorithm; + private static Log log = LogFactory.getLog(SshKeyExchangeFactory.class); + + static { + kexs = new HashMap(); + log.info("Loading key exchange methods"); + kexs.put("diffie-hellman-group1-sha1", DhGroup1Sha1.class); + + try { + // Load external compression from configuration file + if (ConfigurationLoader.isConfigurationAvailable( + SshAPIConfiguration.class)) { + SshAPIConfiguration config = (SshAPIConfiguration) ConfigurationLoader.getConfiguration(SshAPIConfiguration.class); + + if (config != null) { + List list = config.getKeyExchangeExtensions(); + + if (list != null) { + Iterator it = list.iterator(); + + while (it.hasNext()) { + ExtensionAlgorithm algorithm = (ExtensionAlgorithm) it.next(); + String name = algorithm.getAlgorithmName(); + + if (kexs.containsKey(name)) { + log.debug("Standard key exchange " + name + + " is being overidden by " + + algorithm.getImplementationClass()); + } else { + log.debug(algorithm.getAlgorithmName() + + " key exchange is implemented by " + + algorithm.getImplementationClass()); + } + + try { + kexs.put(algorithm.getAlgorithmName(), + ConfigurationLoader.getExtensionClass( + algorithm.getImplementationClass())); + } catch (ClassNotFoundException cnfe) { + log.error("Could not locate " + + algorithm.getImplementationClass()); + } + } + } + + defaultAlgorithm = config.getDefaultKeyExchange(); + } + } + } catch (ConfigurationException ex) { + } + + if ((defaultAlgorithm == null) || !kexs.containsKey(defaultAlgorithm)) { + log.debug( + "The default key exchange is not set! using first in list"); + + Iterator it = kexs.keySet().iterator(); + defaultAlgorithm = (String) it.next(); + } + } + + /** + * Creates a new SshKeyExchangeFactory object. + */ + protected SshKeyExchangeFactory() { + } + + /** + * + */ + public static void initialize() { + } + + /** + * + * + * @return + */ + public static String getDefaultKeyExchange() { + return defaultAlgorithm; + } + + /** + * + * + * @return + */ + public static List getSupportedKeyExchanges() { + return new ArrayList(kexs.keySet()); + } + + /** + * + * + * @param methodName + * + * @return + * + * @throws AlgorithmNotSupportedException + */ + public static SshKeyExchange newInstance(String methodName) + throws AlgorithmNotSupportedException { + try { + return (SshKeyExchange) ((Class) kexs.get(methodName)).newInstance(); + } catch (Exception e) { + throw new AlgorithmNotSupportedException(methodName + + " is not supported!"); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/kex/SshMsgKexDhInit.java b/src/com/sshtools/j2ssh/transport/kex/SshMsgKexDhInit.java new file mode 100644 index 0000000000000000000000000000000000000000..e68832ea138811708c9fe7cfb7473d85763b6a13 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/kex/SshMsgKexDhInit.java @@ -0,0 +1,119 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.kex; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + +import java.math.BigInteger; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshMsgKexDhInit extends SshMessage { + /** */ + protected final static int SSH_MSG_KEXDH_INIT = 30; + + // Stores the e value + private BigInteger e; + + /** + * Creates a new SshMsgKexDhInit object. + * + * @param e + */ + public SshMsgKexDhInit(BigInteger e) { + super(SSH_MSG_KEXDH_INIT); + this.e = e; + } + + /** + * Creates a new SshMsgKexDhInit object. + */ + public SshMsgKexDhInit() { + super(SSH_MSG_KEXDH_INIT); + } + + /** + * + * + * @return + */ + public BigInteger getE() { + return e; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_KEXDH_INIT"; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeBigInteger(e); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing message data: " + + ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + e = bar.readBigInteger(); + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading message data: " + + ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/kex/SshMsgKexDhReply.java b/src/com/sshtools/j2ssh/transport/kex/SshMsgKexDhReply.java new file mode 100644 index 0000000000000000000000000000000000000000..0b52ac369117aa80105211a75d3c692b30fe3a3d --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/kex/SshMsgKexDhReply.java @@ -0,0 +1,151 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.kex; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.InvalidMessageException; +import com.sshtools.j2ssh.transport.SshMessage; + +import java.io.IOException; + +import java.math.BigInteger; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshMsgKexDhReply extends SshMessage { + /** */ + protected final static int SSH_MSG_KEXDH_REPLY = 31; + + // The diffie hellman f value + private BigInteger f; + + // The host key data + private byte[] hostKey; + + // The signature + private byte[] signature; + + /** + * Creates a new SshMsgKexDhReply object. + * + * @param hostKey + * @param f + * @param signature + */ + public SshMsgKexDhReply(byte[] hostKey, BigInteger f, byte[] signature) { + super(SSH_MSG_KEXDH_REPLY); + this.hostKey = hostKey; + this.f = f; + this.signature = signature; + } + + /** + * Creates a new SshMsgKexDhReply object. + */ + public SshMsgKexDhReply() { + super(SSH_MSG_KEXDH_REPLY); + } + + /** + * + * + * @return + */ + public BigInteger getF() { + return f; + } + + /** + * + * + * @return + */ + public byte[] getHostKey() { + return hostKey; + } + + /** + * + * + * @return + */ + public String getMessageName() { + return "SSH_MSG_KEXDH_REPLY"; + } + + /** + * + * + * @return + */ + public byte[] getSignature() { + return signature; + } + + /** + * + * + * @param baw + * + * @throws InvalidMessageException + */ + protected void constructByteArray(ByteArrayWriter baw) + throws InvalidMessageException { + try { + baw.writeBinaryString(hostKey); + baw.writeBigInteger(f); + baw.writeBinaryString(signature); + } catch (IOException ioe) { + throw new InvalidMessageException("Error writing message data: " + + ioe.getMessage()); + } + } + + /** + * + * + * @param bar + * + * @throws InvalidMessageException + */ + protected void constructMessage(ByteArrayReader bar) + throws InvalidMessageException { + try { + hostKey = bar.readBinaryString(); + f = bar.readBigInteger(); + signature = bar.readBinaryString(); + } catch (IOException ioe) { + throw new InvalidMessageException("Error reading message data: " + + ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/Base64EncodedFileFormat.java b/src/com/sshtools/j2ssh/transport/publickey/Base64EncodedFileFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..02995cbca04ce3edcd807f86fe3ebbd3df87c789 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/Base64EncodedFileFormat.java @@ -0,0 +1,253 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.util.Base64; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public abstract class Base64EncodedFileFormat implements SshKeyFormatConversion { + /** */ + protected String begin; + + /** */ + protected String end; + private Map headers = new HashMap(); + private int MAX_LINE_LENGTH = 70; + + /** + * Creates a new Base64EncodedFileFormat object. + * + * @param begin + * @param end + */ + protected Base64EncodedFileFormat(String begin, String end) { + this.begin = begin; + this.end = end; + } + + /** + * + * + * @return + */ + public String getFormatType() { + return "Base64Encoded"; + } + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isFormatted(byte[] formattedKey) { + String test = new String(formattedKey); + + if ((test.indexOf(begin) >= 0) && (test.indexOf(end) > 0)) { + return true; + } else { + return false; + } + } + + /** + * + * + * @param headerTag + * @param headerValue + */ + public void setHeaderValue(String headerTag, String headerValue) { + headers.put(headerTag, headerValue); + } + + /** + * + * + * @param headerTag + * + * @return + */ + public String getHeaderValue(String headerTag) { + return (String) headers.get(headerTag); + } + + /** + * + * + * @param formattedKey + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] getKeyBlob(byte[] formattedKey) throws InvalidSshKeyException { + BufferedReader reader = new BufferedReader(new InputStreamReader( + new ByteArrayInputStream(formattedKey))); + String line; + String headerTag; + String headerValue; + String blob = ""; + int index; + + try { + // Read in the lines looking for the start + while (true) { + line = reader.readLine(); + + if (line == null) { + throw new InvalidSshKeyException("Incorrect file format!"); + } + + if (line.endsWith(begin)) { + break; + } + } + + // Read the headers + while (true) { + line = reader.readLine(); + + if (line == null) { + throw new InvalidSshKeyException("Incorrect file format!"); + } + + index = line.indexOf(": "); + + if (index > 0) { + while (line.endsWith("\\")) { + line = line.substring(0, line.length() - 1); + + String tmp = reader.readLine(); + + if (tmp == null) { + throw new InvalidSshKeyException( + "Incorrect file format!"); + } + + line += tmp; + } + + // Record the header + headerTag = line.substring(0, index); + headerValue = line.substring(index + 2); + headers.put(headerTag, headerValue); + } else { + break; + } + } + + // This is now the public key blob Base64 encoded + ByteArrayWriter baw = new ByteArrayWriter(); + + while (true) { + blob += line; + line = reader.readLine(); + + if (line == null) { + throw new InvalidSshKeyException("Invalid file format!"); + } + + if (line.endsWith(end)) { + break; + } + } + + // Convert the blob to some useful data + return Base64.decode(blob); + } catch (IOException ioe) { + throw new InvalidSshKeyException(); + } + } + + /** + * + * + * @param keyblob + * + * @return + */ + public byte[] formatKey(byte[] keyblob) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + String headerTag; + String headerValue; + String line; + out.write(begin.getBytes()); + out.write('\n'); + + int pos; + Set tags = headers.keySet(); + Iterator it = tags.iterator(); + + while (it.hasNext()) { + headerTag = (String) it.next(); + headerValue = (String) headers.get(headerTag); + + String header = headerTag + ": " + headerValue; + pos = 0; + + while (pos < header.length()) { + line = header.substring(pos, + (((pos + MAX_LINE_LENGTH) < header.length()) + ? (pos + MAX_LINE_LENGTH) : header.length())) + + (((pos + MAX_LINE_LENGTH) < header.length()) ? "\\" : ""); + out.write(line.getBytes()); + out.write('\n'); + pos += MAX_LINE_LENGTH; + } + } + + String encoded = Base64.encodeBytes(keyblob, false); + out.write(encoded.getBytes()); + out.write('\n'); + out.write(end.getBytes()); + out.write('\n'); + + return out.toByteArray(); + } catch (IOException ioe) { + return null; + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/InvalidSshKeyException.java b/src/com/sshtools/j2ssh/transport/publickey/InvalidSshKeyException.java new file mode 100644 index 0000000000000000000000000000000000000000..72f5f2cdab129890e32b4b48dbfb6e9379f2b5de --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/InvalidSshKeyException.java @@ -0,0 +1,53 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.transport.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class InvalidSshKeyException extends TransportProtocolException { + /** + * Creates a new InvalidSshKeyException object. + */ + public InvalidSshKeyException() { + super("The SSH key supplied is invalid"); + } + + /** + * Creates a new InvalidSshKeyException object. + * + * @param msg + */ + public InvalidSshKeyException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/InvalidSshKeySignatureException.java b/src/com/sshtools/j2ssh/transport/publickey/InvalidSshKeySignatureException.java new file mode 100644 index 0000000000000000000000000000000000000000..67fbecf7c5f9bf5148a77b30ebf9cb6c24404d04 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/InvalidSshKeySignatureException.java @@ -0,0 +1,53 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.transport.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class InvalidSshKeySignatureException extends TransportProtocolException { + /** + * Creates a new InvalidSshKeySignatureException object. + */ + public InvalidSshKeySignatureException() { + super("The signature is invalid"); + } + + /** + * Creates a new InvalidSshKeySignatureException object. + * + * @param t + */ + public InvalidSshKeySignatureException(Throwable t) { + super(t.getMessage()); + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/OpenSSHPublicKeyFormat.java b/src/com/sshtools/j2ssh/transport/publickey/OpenSSHPublicKeyFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..6925e6841ab78608e52fbcb07c0ddc34504679b5 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/OpenSSHPublicKeyFormat.java @@ -0,0 +1,169 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.util.Base64; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class OpenSSHPublicKeyFormat implements SshPublicKeyFormat { + String comment = null; + + /** + * Creates a new OpenSSHPublicKeyFormat object. + * + * @param comment + */ + public OpenSSHPublicKeyFormat(String comment) { + setComment(comment); + } + + /** + * Creates a new OpenSSHPublicKeyFormat object. + */ + public OpenSSHPublicKeyFormat() { + } + + /** + * + * + * @param comment + */ + public void setComment(String comment) { + this.comment = comment; + } + + /** + * + * + * @return + */ + public String getComment() { + return comment; + } + + /** + * + * + * @return + */ + public String getFormatType() { + return "OpenSSH-PublicKey"; + } + + /** + * + * + * @param formattedKey + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] getKeyBlob(byte[] formattedKey) throws InvalidSshKeyException { + String temp = new String(formattedKey); + + // Get the algorithm name end index + int i = temp.indexOf(" "); + + if (i > 0) { + String algorithm = temp.substring(0, i); + + if (supportsAlgorithm(algorithm)) { + // Get the keyblob end index + int i2 = temp.indexOf(" ", i + 1); + + if (i2 > i) { + String encoded = temp.substring(i + 1, i2); + + if (temp.length() > i2) { + comment = temp.substring(i2 + 1).trim(); + } + + return Base64.decode(encoded); + } + } + } + + throw new InvalidSshKeyException("Failed to read OpenSSH key format"); + } + + /** + * + * + * @param keyblob + * + * @return + */ + public byte[] formatKey(byte[] keyblob) { + String algorithm = ByteArrayReader.readString(keyblob, 0); + String formatted = algorithm + " " + Base64.encodeBytes(keyblob, true); + + if (comment != null) { + formatted += (" " + comment); + } + + return formatted.getBytes(); + } + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isFormatted(byte[] formattedKey) { + String test = new String(formattedKey).trim(); + + if (test.startsWith("ssh-dss") || test.startsWith("ssh-rsa")) { + return true; + } else { + return false; + } + } + + /** + * + * + * @param algorithm + * + * @return + */ + public boolean supportsAlgorithm(String algorithm) { + if (algorithm.equals("ssh-dss") || algorithm.equals("ssh-rsa")) { + return true; + } else { + return false; + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SECSHPublicKeyFormat.java b/src/com/sshtools/j2ssh/transport/publickey/SECSHPublicKeyFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..8ead6c195a000bc6b02a1ebfcc05bf77e3b78b15 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SECSHPublicKeyFormat.java @@ -0,0 +1,98 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public class SECSHPublicKeyFormat extends Base64EncodedFileFormat + implements SshPublicKeyFormat { + private static String BEGIN = "---- BEGIN SSH2 PUBLIC KEY ----"; + private static String END = "---- END SSH2 PUBLIC KEY ----"; + + /** + * Creates a new SECSHPublicKeyFormat object. + * + * @param subject + * @param comment + */ + public SECSHPublicKeyFormat(String subject, String comment) { + super(BEGIN, END); + setHeaderValue("Subject", subject); + setComment(comment); + } + + /** + * Creates a new SECSHPublicKeyFormat object. + */ + public SECSHPublicKeyFormat() { + super(BEGIN, END); + } + + /** + * + * + * @param comment + */ + public void setComment(String comment) { + setHeaderValue("Comment", + (comment.trim().startsWith("\"") ? "" : "\"") + comment.trim() + + (comment.trim().endsWith("\"") ? "" : "\"")); + } + + /** + * + * + * @return + */ + public String getComment() { + return getHeaderValue("Comment"); + } + + /** + * + * + * @return + */ + public String getFormatType() { + return "SECSH-PublicKey-" + super.getFormatType(); + } + + /** + * + * + * @param algorithm + * + * @return + */ + public boolean supportsAlgorithm(String algorithm) { + return SshKeyPairFactory.supportsKey(algorithm); + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshKeyFormatConversion.java b/src/com/sshtools/j2ssh/transport/publickey/SshKeyFormatConversion.java new file mode 100644 index 0000000000000000000000000000000000000000..127d450f82329bc05dbe3daa155472da9d55ba38 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshKeyFormatConversion.java @@ -0,0 +1,71 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.16 $ + */ +public interface SshKeyFormatConversion { + /** + * + * + * @return + */ + public String getFormatType(); + + /** + * + * + * @param formattedKey + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] getKeyBlob(byte[] formattedKey) throws InvalidSshKeyException; + + /** + * + * + * @param keyBlob + * + * @return + */ + public byte[] formatKey(byte[] keyBlob); + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isFormatted(byte[] formattedKey); +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshKeyGenerator.java b/src/com/sshtools/j2ssh/transport/publickey/SshKeyGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..417f40bd258371b5244b87f27334d954d0982a86 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshKeyGenerator.java @@ -0,0 +1,377 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.SshThread; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import java.lang.reflect.Method; + + +/*import java.util.logging.FileHandler; + import java.util.logging.Handler; + import java.util.logging.Level; + import java.util.logging.Logger; + import java.util.logging.SimpleFormatter;*/ +public class SshKeyGenerator { + private static String filename = null; + private static String type = "dsa"; + private static int bits = 1024; + private static boolean useGUI; + private static boolean guiAvailable; + private static boolean toOpenSSH = false; + private static boolean toSECSH = false; + private static boolean changePass = false; + + // Test if the GUI is available + static { + try { + Class.forName("com.sshtools.j2ssh.keygen.Main"); + guiAvailable = true; + } catch (ClassNotFoundException cnfe) { + } + } + + /** + * Creates a new SshKeyGenerator object. + */ + public SshKeyGenerator() { + } + + /** + * + * + * @param type + * @param bits + * @param filename + * @param username + * @param passphrase + * + * @throws IOException + */ + public void generateKeyPair(String type, int bits, String filename, + String username, String passphrase) throws IOException { + System.out.println("****Sshtools.com SSH Key Pair Generator****"); + + String keyType = type; + + if (keyType.equalsIgnoreCase("DSA")) { + keyType = "ssh-dss"; + } + + if (keyType.equalsIgnoreCase("RSA")) { + keyType = "ssh-rsa"; + } + + final SshKeyPair pair = SshKeyPairFactory.newInstance(keyType); + System.out.println("Generating " + String.valueOf(bits) + " bit " + + keyType + " key pair"); + + Thread thread = new SshThread(new Runnable() { + public void run() { + pair.generate(SshKeyGenerator.this.bits); + } + }, "Key generator", true); + thread.start(); + + while (thread.isAlive()) { + System.out.print("."); + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + + System.out.println(); + System.out.println("Creating Public Key file " + filename + ".pub"); + + // Now save the files + SshPublicKeyFile pub = SshPublicKeyFile.create(pair.getPublicKey(), + new SECSHPublicKeyFormat(username, + String.valueOf(bits) + "-bit " + type)); + FileOutputStream out = new FileOutputStream(filename + ".pub"); + out.write(pub.getBytes()); + out.close(); + System.out.println("Generating Private Key file " + filename); + + if (passphrase == null) { + passphrase = promptForPassphrase(true); + } + + SshPrivateKeyFile prv = SshPrivateKeyFile.create(pair.getPrivateKey(), + passphrase, + new SshtoolsPrivateKeyFormat(username, + String.valueOf(bits) + "-bit " + type)); + out = new FileOutputStream(filename); + out.write(prv.getBytes()); + out.close(); + } + + /** + * + * + * @param args + */ + public static void main(String[] args) { + try { + processCommandLine(args); + + // Setup a logfile + + /*Handler fh = new FileHandler("ssh-keygen.log"); + fh.setFormatter(new SimpleFormatter()); + Logger.getLogger("com.sshtools").setUseParentHandlers(false); + Logger.getLogger("com.sshtools").addHandler(fh); + Logger.getLogger("com.sshtools").setLevel(Level.ALL);*/ + if (useGUI) { + Class c = Class.forName("com.sshtools.j2ssh.keygen.Main"); + Method m = c.getMethod("main", new Class[] { args.getClass() }); + m.invoke(null, new Object[] { new String[] { } }); + } else { + File f = new File(filename); + + if (filename == null) { + System.err.print("You must supply a valid file to convert!"); + System.exit(1); + } + + if (toOpenSSH || toSECSH) { + if (!f.exists()) { + System.err.print("The file " + f.getAbsolutePath() + + " does not exist!"); + System.exit(1); + } + + try { + if (toOpenSSH) { + System.out.print(convertPublicKeyFile(f, + new OpenSSHPublicKeyFormat())); + } else { + System.out.print(convertPublicKeyFile(f, + new SECSHPublicKeyFormat())); + } + } catch (InvalidSshKeyException e) { + System.err.println("The key format is invalid!"); + } catch (IOException ioe) { + System.err.println( + "An error occurs whilst reading the file " + + f.getAbsolutePath()); + } + + System.exit(0); + } + + if (changePass) { + if (!f.exists()) { + System.err.print("The file " + f.getAbsolutePath() + + " does not exist!"); + System.exit(1); + } + + changePassphrase(f); + } else { + SshKeyGenerator generator = new SshKeyGenerator(); + String username = System.getProperty("user.name"); + generator.generateKeyPair(type, bits, filename, username, + null); + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * + * + * @param args + */ + public static void processCommandLine(String[] args) { + if (args.length > 0) { + for (int i = 0; i < args.length; i++) { + if (args[i].equalsIgnoreCase("-b")) { + bits = Integer.parseInt(args[++i]); + } else if (args[i].equalsIgnoreCase("-t")) { + type = args[++i]; + } else if (args[i].equalsIgnoreCase("-p")) { + changePass = true; + } else if (args[i].equalsIgnoreCase("-g") && guiAvailable) { + useGUI = true; + } else if (args[i].equalsIgnoreCase("-i")) { + toOpenSSH = true; + } else if (args[i].equalsIgnoreCase("-e")) { + toSECSH = true; + } else if (!args[i].startsWith("-")) { + if (filename != null) { + printUsage(); + System.exit(1); + } + + filename = args[i]; + } + } + } + + if (!useGUI && (filename == null)) { + printUsage(); + System.exit(0); + } + } + + private static void changePassphrase(File f) { + System.out.println("Opening Private Key file " + f.getAbsolutePath()); + + try { + System.out.println("Opening Private Key file " + + f.getAbsolutePath()); + + String oldPassphrase = promptForPassphrase(false); + String newPassphrase = promptForPassphrase(true); + changePassphrase(f, oldPassphrase, newPassphrase); + } catch (InvalidSshKeyException e) { + System.err.println("The key format is invalid!"); + } catch (IOException ioe) { + System.err.println("An error occurs whilst reading the file " + + f.getAbsolutePath()); + } + } + + /** + * + * + * @param f + * @param oldPassphrase + * @param newPassphrase + * + * @throws IOException + * @throws InvalidSshKeyException + */ + public static void changePassphrase(File f, String oldPassphrase, + String newPassphrase) throws IOException, InvalidSshKeyException { + // Open up the file with its current format + SshPrivateKeyFile file = SshPrivateKeyFile.parse(f); + System.out.println("Saving Private Key file with new passphrase"); + file.changePassphrase(oldPassphrase, newPassphrase); + + FileOutputStream out = null; + + try { + out = new FileOutputStream(f); + out.write(file.getBytes()); + } finally { + if (out != null) { + out.close(); + } + } + } + + /** + * + * + * @param f + * @param convert + * + * @return + * + * @throws InvalidSshKeyException + * @throws IOException + */ + public static String convertPublicKeyFile(File f, SshPublicKeyFormat convert) + throws InvalidSshKeyException, IOException { + // Open up the file with its current format + SshPublicKeyFile file = SshPublicKeyFile.parse(f); + + // Set the new format + file.setFormat(convert); + + // Output to stdout + return file.toString(); + } + + private static void printUsage() { + System.out.println("Usage: SshKeyGenerator [options] filename"); + System.out.println("Options:"); + System.out.println( + "-b bits Number of bits in the key to create."); + System.out.println( + "-e Convert OpenSSH to IETF SECSH key file."); + System.out.println( + "-i Convert IETF SECSH to OpenSSH key file."); + System.out.println("-t type The type of key to create."); + System.out.println( + "-p Change the passphrase of the private key file."); + + if (guiAvailable) { + System.out.println("-g \t\tUse GUI to create key"); + } + } + + private static String promptForPassphrase(boolean confirm) + throws IOException { + // Confirm the passphrase + BufferedReader reader = new BufferedReader(new InputStreamReader( + System.in)); + String pass1 = ""; + String pass2 = ""; + + while (true) { + System.out.print("Enter passphrase: "); + pass1 = reader.readLine(); + + if (!confirm) { + break; + } + + System.out.print("Confirm passphrase: "); + pass2 = reader.readLine(); + + if (pass1.equals(pass2)) { + if (pass1.trim().length() == 0) { + System.out.print( + "You supplied an empty passphrase, are you sure? [Yes|No]: "); + pass2 = reader.readLine(); + + if (pass2.equalsIgnoreCase("YES")) { + break; + } + } else { + break; + } + } else { + System.out.println( + "The passphrases supplied were not indentical! Try again"); + } + } + + return pass1; + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshKeyPair.java b/src/com/sshtools/j2ssh/transport/publickey/SshKeyPair.java new file mode 100644 index 0000000000000000000000000000000000000000..8ad48592b51bde2a16b892f2a0cc500b2f0b8bdc --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshKeyPair.java @@ -0,0 +1,136 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public abstract class SshKeyPair { + private SshPrivateKey prv; + private SshPublicKey pub; + + /** + * Creates a new SshKeyPair object. + */ + public SshKeyPair() { + } + + /** + * + * + * @param bits + */ + public abstract void generate(int bits); + + /** + * + * + * @param key + */ + public void setPrivateKey(SshPrivateKey key) { + this.prv = key; + this.pub = key.getPublicKey(); + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public SshPrivateKey setPrivateKey(byte[] encoded) + throws InvalidSshKeyException { + setPrivateKey(decodePrivateKey(encoded)); + + return this.prv; + } + + /** + * + * + * @return + */ + public SshPrivateKey getPrivateKey() { + return prv; + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public SshPublicKey setPublicKey(byte[] encoded) + throws InvalidSshKeyException { + this.pub = decodePublicKey(encoded); + this.prv = null; + + return this.pub; + } + + /** + * + * + * @return + */ + public SshPublicKey getPublicKey() { + return pub; + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public abstract SshPrivateKey decodePrivateKey(byte[] encoded) + throws InvalidSshKeyException; + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public abstract SshPublicKey decodePublicKey(byte[] encoded) + throws InvalidSshKeyException; +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshKeyPairFactory.java b/src/com/sshtools/j2ssh/transport/publickey/SshKeyPairFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3c7b9bc95a4bbdd09e8b8f8de9229205504fc6f7 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshKeyPairFactory.java @@ -0,0 +1,235 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.ExtensionAlgorithm; +import com.sshtools.j2ssh.configuration.SshAPIConfiguration; +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; +import com.sshtools.j2ssh.transport.publickey.dsa.SshDssKeyPair; +import com.sshtools.j2ssh.transport.publickey.rsa.SshRsaKeyPair; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.26 $ + */ +public class SshKeyPairFactory { + private static Map pks; + private static String defaultAlgorithm; + private static Log log = LogFactory.getLog(SshKeyPairFactory.class); + + static { + pks = new HashMap(); + log.info("Loading public key algorithms"); + pks.put("ssh-dss", SshDssKeyPair.class); + pks.put("ssh-rsa", SshRsaKeyPair.class); + + try { + // Load external pks from configuration file + if (ConfigurationLoader.isConfigurationAvailable( + SshAPIConfiguration.class)) { + SshAPIConfiguration config = (SshAPIConfiguration) ConfigurationLoader.getConfiguration(SshAPIConfiguration.class); + + if (config != null) { + List list = config.getPublicKeyExtensions(); + + if (list != null) { + Iterator it = list.iterator(); + + while (it.hasNext()) { + ExtensionAlgorithm algorithm = (ExtensionAlgorithm) it.next(); + String name = algorithm.getAlgorithmName(); + + if (pks.containsKey(name)) { + log.debug("Standard public key " + name + + " is being overidden by " + + algorithm.getImplementationClass()); + } else { + log.debug(algorithm.getAlgorithmName() + + " public key is implemented by " + + algorithm.getImplementationClass()); + } + + try { + pks.put(algorithm.getAlgorithmName(), + ConfigurationLoader.getExtensionClass( + algorithm.getImplementationClass())); + } catch (ClassNotFoundException cnfe) { + log.error("Could not locate " + + algorithm.getImplementationClass()); + } + } + } + + defaultAlgorithm = config.getDefaultPublicKey(); + } + } + } catch (ConfigurationException ex) { + } + + if ((defaultAlgorithm == null) || !pks.containsKey(defaultAlgorithm)) { + log.debug("The default public key is not set! using first in list"); + + Iterator it = pks.keySet().iterator(); + defaultAlgorithm = (String) it.next(); + } + } + + /** + * Creates a new SshKeyPairFactory object. + */ + protected SshKeyPairFactory() { + } + + /** + * + */ + public static void initialize() { + } + + /** + * + * + * @return + */ + public static String getDefaultPublicKey() { + return defaultAlgorithm; + } + + /** + * + * + * @return + */ + public static List getSupportedKeys() { + // Get the list of pks + return new ArrayList(pks.keySet()); + } + + /** + * + * + * @param methodName + * + * @return + * + * @throws AlgorithmNotSupportedException + */ + public static SshKeyPair newInstance(String methodName) + throws AlgorithmNotSupportedException { + try { + return (SshKeyPair) ((Class) pks.get(methodName)).newInstance(); + } catch (Exception e) { + throw new AlgorithmNotSupportedException(methodName + + " is not supported!"); + } + } + + /** + * + * + * @param algorithm + * + * @return + */ + public static boolean supportsKey(String algorithm) { + return pks.containsKey(algorithm); + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + * @throws AlgorithmNotSupportedException + */ + public static SshPrivateKey decodePrivateKey(byte[] encoded) + throws InvalidSshKeyException, AlgorithmNotSupportedException { + try { + ByteArrayReader bar = new ByteArrayReader(encoded); + String algorithm = bar.readString(); + + if (supportsKey(algorithm)) { + SshKeyPair pair = newInstance(algorithm); + + return pair.decodePrivateKey(encoded); + } else { + throw new AlgorithmNotSupportedException(algorithm + + " is not supported"); + } + } catch (IOException ioe) { + throw new InvalidSshKeyException(ioe.getMessage()); + } + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + * @throws AlgorithmNotSupportedException + */ + public static SshPublicKey decodePublicKey(byte[] encoded) + throws InvalidSshKeyException, AlgorithmNotSupportedException { + try { + ByteArrayReader bar = new ByteArrayReader(encoded); + String algorithm = bar.readString(); + + if (supportsKey(algorithm)) { + SshKeyPair pair = newInstance(algorithm); + + return pair.decodePublicKey(encoded); + } else { + throw new AlgorithmNotSupportedException(algorithm + + " is not supported"); + } + } catch (IOException ioe) { + throw new InvalidSshKeyException(ioe.getMessage()); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKey.java b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKey.java new file mode 100644 index 0000000000000000000000000000000000000000..f8323c2eae9111a1cfe731342b93179ad91d4e1a --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKey.java @@ -0,0 +1,79 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public abstract class SshPrivateKey { + /** + * Creates a new SshPrivateKey object. + */ + public SshPrivateKey() { + } + + /** + * + * + * @return + */ + public abstract String getAlgorithmName(); + + /** + * + * + * @return + */ + public abstract int getBitLength(); + + /** + * + * + * @return + */ + public abstract byte[] getEncoded(); + + /** + * + * + * @return + */ + public abstract SshPublicKey getPublicKey(); + + /** + * + * + * @param data + * + * @return + */ + public abstract byte[] generateSignature(byte[] data) + throws InvalidSshKeySignatureException; +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFile.java b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFile.java new file mode 100644 index 0000000000000000000000000000000000000000..1b59318e1960763acf50b55c0daf0eda1f60107d --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFile.java @@ -0,0 +1,255 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.transport.AlgorithmNotSupportedException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import java.util.Iterator; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.21 $ + */ +public class SshPrivateKeyFile { + private static Log log = LogFactory.getLog(SshPrivateKeyFile.class); + private SshPrivateKeyFormat format; + private byte[] keyblob; + + /** + * Creates a new SshPrivateKeyFile object. + * + * @param keyblob + * @param format + */ + protected SshPrivateKeyFile(byte[] keyblob, SshPrivateKeyFormat format) { + this.keyblob = keyblob; + this.format = format; + } + + /** + * + * + * @return + */ + public byte[] getBytes() { + return keyblob; + } + + /** + * + * + * @param passphrase + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] getKeyBlob(String passphrase) throws InvalidSshKeyException { + return format.decryptKeyblob(keyblob, passphrase); + } + + /** + * + * + * @param oldPassphrase + * @param newPassphrase + * + * @throws InvalidSshKeyException + */ + public void changePassphrase(String oldPassphrase, String newPassphrase) + throws InvalidSshKeyException { + byte[] raw = format.decryptKeyblob(keyblob, oldPassphrase); + keyblob = format.encryptKeyblob(raw, newPassphrase); + } + + /** + * + * + * @param formattedKey + * + * @return + * + * @throws InvalidSshKeyException + */ + public static SshPrivateKeyFile parse(byte[] formattedKey) + throws InvalidSshKeyException { + if (formattedKey == null) { + throw new InvalidSshKeyException("Key data is null"); + } + + log.info("Parsing private key file"); + + // Try the default private key format + SshPrivateKeyFormat format; + format = SshPrivateKeyFormatFactory.newInstance(SshPrivateKeyFormatFactory.getDefaultFormatType()); + + boolean valid = format.isFormatted(formattedKey); + + if (!valid) { + log.info( + "Private key is not in the default format, attempting parse with other supported formats"); + + Iterator it = SshPrivateKeyFormatFactory.getSupportedFormats() + .iterator(); + String ft; + + while (it.hasNext() && !valid) { + ft = (String) it.next(); + log.debug("Attempting " + ft); + format = SshPrivateKeyFormatFactory.newInstance(ft); + valid = format.isFormatted(formattedKey); + } + } + + if (valid) { + return new SshPrivateKeyFile(formattedKey, format); + } else { + throw new InvalidSshKeyException( + "The key format is not a supported format"); + } + } + + /** + * + * + * @param keyfile + * + * @return + * + * @throws InvalidSshKeyException + * @throws IOException + */ + public static SshPrivateKeyFile parse(File keyfile) + throws InvalidSshKeyException, IOException { + FileInputStream in = new FileInputStream(keyfile); + byte[] data = null; + + try { + data = new byte[in.available()]; + in.read(data); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException ex) { + } + } + + return parse(data); + } + + /** + * + * + * @return + */ + public boolean isPassphraseProtected() { + return format.isPassphraseProtected(keyblob); + } + + /*public void changePassphrase(String oldPassphrase, String newPassphrase) + throws InvalidSshKeyException { + keyblob = format.changePassphrase(keyblob, oldPassphrase, newPassphrase); + }*/ + public static SshPrivateKeyFile create(SshPrivateKey key, + String passphrase, SshPrivateKeyFormat format) + throws InvalidSshKeyException { + byte[] keyblob = format.encryptKeyblob(key.getEncoded(), passphrase); + + return new SshPrivateKeyFile(keyblob, format); + } + + /** + * + * + * @param newFormat + * @param passphrase + * + * @throws InvalidSshKeyException + */ + public void setFormat(SshPrivateKeyFormat newFormat, String passphrase) + throws InvalidSshKeyException { + byte[] raw = this.format.decryptKeyblob(keyblob, passphrase); + format = newFormat; + keyblob = format.encryptKeyblob(raw, passphrase); + } + + /** + * + * + * @return + */ + public SshPrivateKeyFormat getFormat() { + return format; + } + + /** + * + * + * @param passphrase + * + * @return + * + * @throws InvalidSshKeyException + */ + public SshPrivateKey toPrivateKey(String passphrase) + throws InvalidSshKeyException { + try { + byte[] raw = format.decryptKeyblob(keyblob, passphrase); + SshKeyPair pair = SshKeyPairFactory.newInstance(getAlgorithm(raw)); + + return pair.decodePrivateKey(raw); + } catch (AlgorithmNotSupportedException anse) { + throw new InvalidSshKeyException( + "The public key algorithm for this private key is not supported"); + } + } + + /** + * + * + * @return + */ + public String toString() { + return new String(keyblob); + } + + private String getAlgorithm(byte[] raw) { + return ByteArrayReader.readString(raw, 0); + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFormat.java b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..8d84023f9ff18f9a2dbb6825aabdae378cc84e64 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFormat.java @@ -0,0 +1,95 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public interface SshPrivateKeyFormat { + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isPassphraseProtected(byte[] formattedKey); + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isFormatted(byte[] formattedKey); + + /** + * + * + * @param formattedKey + * @param passphrase + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] decryptKeyblob(byte[] formattedKey, String passphrase) + throws InvalidSshKeyException; + + /** + * + * + * @param keyblob + * @param passphrase + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] encryptKeyblob(byte[] keyblob, String passphrase) + throws InvalidSshKeyException; + + /** + * + * + * @param algorithm + * + * @return + */ + public boolean supportsAlgorithm(String algorithm); + + /** + * + * + * @return + */ + public String getFormatType(); +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFormatFactory.java b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFormatFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..87b7b78f31a47be6f9da16682374d2356aae2a3a --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPrivateKeyFormatFactory.java @@ -0,0 +1,171 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.IOUtil; +import com.sshtools.j2ssh.openssh.*; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.InputStream; + +import java.net.URL; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.26 $ + */ +public class SshPrivateKeyFormatFactory { + private static String defaultFormat; + private static HashMap formatTypes; + private static Log log = LogFactory.getLog(SshPrivateKeyFormatFactory.class); + private static Vector types; + + static { + log.info("Loading private key formats"); + + List formats = new ArrayList(); + types = new Vector(); + formatTypes = new HashMap(); + formats.add(SshtoolsPrivateKeyFormat.class.getName()); + formats.add(OpenSSHPrivateKeyFormat.class.getName()); + + defaultFormat = "SSHTools-PrivateKey-Base64Encoded"; + + try { + Enumeration en = ConfigurationLoader.getExtensionClassLoader() + .getResources("j2ssh.privatekey"); + URL url; + Properties properties = new Properties(); + InputStream in; + + while ((en != null) && en.hasMoreElements()) { + url = (URL) en.nextElement(); + in = url.openStream(); + properties.load(in); + IOUtil.closeStream(in); + + int num = 1; + String name = ""; + Class cls; + + while (properties.getProperty("privatekey.name." + + String.valueOf(num)) != null) { + name = properties.getProperty("privatekey.name." + + String.valueOf(num)); + formats.add(properties.getProperty("privatekey.class." + + String.valueOf(num))); + + num++; + } + } + } catch (Throwable t) { + } + + SshPrivateKeyFormat f; + + Iterator it = formats.iterator(); + String classname; + + while (it.hasNext()) { + classname = (String) it.next(); + + try { + Class cls = ConfigurationLoader.getExtensionClass(classname); + f = (SshPrivateKeyFormat) cls.newInstance(); + log.debug("Installing " + f.getFormatType() + + " private key format"); + formatTypes.put(f.getFormatType(), cls); + types.add(f.getFormatType()); + } catch (Throwable t) { + log.warn("Private key format implemented by " + classname + + " will not be available", t); + } + } + } + + /** + * + * + * @return + */ + public static List getSupportedFormats() { + return types; + } + + public static void initialize() { + } + + /** + * + * + * @param type + * + * @return + * + * @throws InvalidSshKeyException + */ + public static SshPrivateKeyFormat newInstance(String type) + throws InvalidSshKeyException { + try { + if (formatTypes.containsKey(type)) { + return (SshPrivateKeyFormat) ((Class) formatTypes.get(type)).newInstance(); + } else { + throw new InvalidSshKeyException("The format type " + type + + " is not supported"); + } + } catch (IllegalAccessException iae) { + throw new InvalidSshKeyException( + "Illegal access to class implementation of " + type); + } catch (InstantiationException ie) { + throw new InvalidSshKeyException( + "Failed to create instance of format type " + type); + } + } + + /** + * + * + * @return + */ + public static String getDefaultFormatType() { + return defaultFormat; + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPublicKey.java b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKey.java new file mode 100644 index 0000000000000000000000000000000000000000..e79436c32d3c7147391dd44e89551864afa2388f --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKey.java @@ -0,0 +1,124 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.util.Hash; + +import java.security.NoSuchAlgorithmException; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.24 $ + */ +public abstract class SshPublicKey { + /** + * + * + * @return + */ + public abstract String getAlgorithmName(); + + /** + * + * + * @return + */ + public abstract int getBitLength(); + + /** + * + * + * @return + */ + public abstract byte[] getEncoded(); + + /** + * + * + * @return + */ + public String getFingerprint() { + try { + Hash md5 = new Hash("MD5"); + md5.putBytes(getEncoded()); + + byte[] digest = md5.doFinal(); + int bits = getBitLength(); + bits = (((bits % 8) != 0) ? (bits += (bits % 8)) : bits); + + String ret = String.valueOf(bits); + + for (int i = 0; i < digest.length; i++) { + ret += (((i == 0) ? ":" : "") + " " + + Integer.toHexString(digest[i] & 0xFF)); + } + + return ret; + } catch (NoSuchAlgorithmException nsae) { + return null; + } + } + + /** + * + * + * @param obj + * + * @return + */ + public boolean equals(Object obj) { + if (obj instanceof SshPublicKey) { + return (getFingerprint().compareTo(((SshPublicKey) obj).getFingerprint()) == 0); + } + + return false; + } + + /** + * + * + * @return + */ + public int hashCode() { + return getFingerprint().hashCode(); + } + + /** + * + * + * @param signature + * @param exchangeHash + * + * @return + * + * @throws InvalidSshKeySignatureException + */ + public abstract boolean verifySignature(byte[] signature, + byte[] exchangeHash) throws InvalidSshKeySignatureException; +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFile.java b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFile.java new file mode 100644 index 0000000000000000000000000000000000000000..b34a929a8f9709cfaa713f002d8b448d4af4efd6 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFile.java @@ -0,0 +1,241 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.io.ByteArrayReader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import java.util.Iterator; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.20 $ + */ +public class SshPublicKeyFile { + private static Log log = LogFactory.getLog(SshPublicKeyFile.class); + private SshPublicKeyFormat format; + private byte[] keyblob; + private String comment; + + /** + * Creates a new SshPublicKeyFile object. + * + * @param keyblob + * @param format + */ + protected SshPublicKeyFile(byte[] keyblob, SshPublicKeyFormat format) { + this.keyblob = keyblob; + this.format = format; + } + + /** + * + * + * @return + */ + public byte[] getBytes() { + return format.formatKey(keyblob); + } + + /** + * + * + * @return + */ + public String getComment() { + return comment; + } + + /** + * + * + * @param comment + */ + public void setComment(String comment) { + this.comment = comment; + } + + /** + * + * + * @return + */ + public byte[] getKeyBlob() { + return keyblob; + } + + /** + * + * + * @param key + * @param format + * + * @return + */ + public static SshPublicKeyFile create(SshPublicKey key, + SshPublicKeyFormat format) { + SshPublicKeyFile file = new SshPublicKeyFile(key.getEncoded(), format); + file.setComment(format.getComment()); + + return file; + } + + /** + * + * + * @param keyfile + * + * @return + * + * @throws InvalidSshKeyException + * @throws IOException + */ + public static SshPublicKeyFile parse(File keyfile) + throws InvalidSshKeyException, IOException { + FileInputStream in = new FileInputStream(keyfile); + byte[] data = new byte[in.available()]; + in.read(data); + in.close(); + + return parse(data); + } + + /** + * + * + * @param formattedKey + * + * @return + * + * @throws InvalidSshKeyException + */ + public static SshPublicKeyFile parse(byte[] formattedKey) + throws InvalidSshKeyException { + log.info("Parsing public key file"); + + // Try the default private key format + SshPublicKeyFormat format; + format = SshPublicKeyFormatFactory.newInstance(SshPublicKeyFormatFactory.getDefaultFormatType()); + + boolean valid = format.isFormatted(formattedKey); + + if (!valid) { + log.info( + "Public key is not in the default format, attempting parse with other supported formats"); + + Iterator it = SshPublicKeyFormatFactory.getSupportedFormats() + .iterator(); + String ft; + + while (it.hasNext() && !valid) { + ft = (String) it.next(); + log.debug("Attempting " + ft); + format = SshPublicKeyFormatFactory.newInstance(ft); + valid = format.isFormatted(formattedKey); + } + } + + if (valid) { + SshPublicKeyFile file = new SshPublicKeyFile(format.getKeyBlob( + formattedKey), format); + file.setComment(format.getComment()); + + return file; + } else { + throw new InvalidSshKeyException( + "The key format is not a supported format"); + } + } + + /** + * + * + * @return + */ + public String getAlgorithm() { + return ByteArrayReader.readString(keyblob, 0); + } + + /** + * + * + * @param newFormat + * + * @throws InvalidSshKeyException + */ + public void setFormat(SshPublicKeyFormat newFormat) + throws InvalidSshKeyException { + if (newFormat.supportsAlgorithm(getAlgorithm())) { + newFormat.setComment(format.getComment()); + this.format = newFormat; + } else { + throw new InvalidSshKeyException( + "The format does not support the public key algorithm"); + } + } + + /** + * + * + * @return + */ + public SshPublicKeyFormat getFormat() { + return format; + } + + /** + * + * + * @return + * + * @throws IOException + */ + public SshPublicKey toPublicKey() throws IOException { + ByteArrayReader bar = new ByteArrayReader(keyblob); + String type = bar.readString(); + SshKeyPair pair = SshKeyPairFactory.newInstance(type); + + return pair.decodePublicKey(keyblob); + } + + /** + * + * + * @return + */ + public String toString() { + return new String(format.formatKey(keyblob)); + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFormat.java b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..e5d906853983de524b7be820ea45c2c38099d9cd --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFormat.java @@ -0,0 +1,94 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public interface SshPublicKeyFormat { + /** + * + * + * @param comment + */ + public void setComment(String comment); + + /** + * + * + * @return + */ + public String getComment(); + + /** + * + * + * @param algorithm + * + * @return + */ + public boolean supportsAlgorithm(String algorithm); + + /** + * + * + * @param keyblob + * + * @return + */ + public byte[] formatKey(byte[] keyblob); + + /** + * + * + * @param formattedKey + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] getKeyBlob(byte[] formattedKey) throws InvalidSshKeyException; + + /** + * + * + * @return + */ + public String getFormatType(); + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isFormatted(byte[] formattedKey); +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFormatFactory.java b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFormatFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..4bf5cc5ce521cb30693e1416a9741c017019c4fa --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshPublicKeyFormatFactory.java @@ -0,0 +1,137 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.configuration.ConfigurationException; +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.configuration.SshAPIConfiguration; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class SshPublicKeyFormatFactory { + private static String defaultFormat; + private static HashMap formatTypes = new HashMap(); + private static Log log = LogFactory.getLog(SshPublicKeyFormatFactory.class); + private static Vector types = new Vector(); + + static { + List formats = new ArrayList(); + formats.add(SECSHPublicKeyFormat.class.getName()); + formats.add(OpenSSHPublicKeyFormat.class.getName()); + defaultFormat = "SECSH-PublicKey-Base64Encoded"; + + try { + if (ConfigurationLoader.isConfigurationAvailable( + SshAPIConfiguration.class)) { + SshAPIConfiguration config = (SshAPIConfiguration) ConfigurationLoader.getConfiguration(SshAPIConfiguration.class); + defaultFormat = config.getDefaultPublicKeyFormat(); + formats.addAll(config.getPublicKeyFormats()); + } + } catch (ConfigurationException ex) { + } + + log.debug("Default public key format will be " + defaultFormat); + + SshPublicKeyFormat f; + Iterator it = formats.iterator(); + String classname; + + while (it.hasNext()) { + classname = (String) it.next(); + + try { + Class cls = ConfigurationLoader.getExtensionClass(classname); + f = (SshPublicKeyFormat) cls.newInstance(); + log.debug("Installing " + f.getFormatType() + + " public key format"); + formatTypes.put(f.getFormatType(), cls); + types.add(f.getFormatType()); + } catch (Exception iae) { + log.warn("Public key format implemented by " + classname + + " will not be available", iae); + } + } + } + + /** + * + * + * @return + */ + public static List getSupportedFormats() { + return types; + } + + /** + * + * + * @param type + * + * @return + * + * @throws InvalidSshKeyException + */ + public static SshPublicKeyFormat newInstance(String type) + throws InvalidSshKeyException { + try { + if (formatTypes.containsKey(type)) { + return (SshPublicKeyFormat) ((Class) formatTypes.get(type)).newInstance(); + } else { + throw new InvalidSshKeyException("The format type " + type + + " is not supported"); + } + } catch (IllegalAccessException iae) { + throw new InvalidSshKeyException( + "Illegal access to class implementation of " + type); + } catch (InstantiationException ie) { + throw new InvalidSshKeyException( + "Failed to create instance of format type " + type); + } + } + + /** + * + * + * @return + */ + public static String getDefaultFormatType() { + return defaultFormat; + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/SshtoolsPrivateKeyFormat.java b/src/com/sshtools/j2ssh/transport/publickey/SshtoolsPrivateKeyFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..f530ac7745fb5b62819a20d11f82177e6c01aae4 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/SshtoolsPrivateKeyFormat.java @@ -0,0 +1,252 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.util.Hash; + +import java.io.IOException; + +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.spec.KeySpec; + +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.22 $ + */ +public class SshtoolsPrivateKeyFormat extends Base64EncodedFileFormat + implements SshPrivateKeyFormat { + private static String BEGIN = "---- BEGIN SSHTOOLS ENCRYPTED PRIVATE KEY ----"; + private static String END = "---- END SSHTOOLS ENCRYPTED PRIVATE KEY ----"; + private int cookie = 0x52f37abe; + + /** + * Creates a new SshtoolsPrivateKeyFormat object. + * + * @param subject + * @param comment + */ + public SshtoolsPrivateKeyFormat(String subject, String comment) { + super(BEGIN, END); + setHeaderValue("Subject", subject); + setHeaderValue("Comment", comment); + } + + /** + * Creates a new SshtoolsPrivateKeyFormat object. + */ + public SshtoolsPrivateKeyFormat() { + super(BEGIN, END); + } + + /** + * + * + * @return + */ + public String getFormatType() { + return "SSHTools-PrivateKey-" + super.getFormatType(); + } + + /** + * + * + * @param formattedKey + * + * @return + */ + public boolean isPassphraseProtected(byte[] formattedKey) { + try { + ByteArrayReader bar = new ByteArrayReader(getKeyBlob(formattedKey)); + String type = bar.readString(); + + if (type.equals("none")) { + return false; + } + + if (type.equalsIgnoreCase("3des-cbc")) { + return true; + } + } catch (IOException ioe) { + } + + return false; + } + + /** + * + * + * @param formattedKey + * @param passphrase + * + * @return + * + * @throws InvalidSshKeyException + */ + public byte[] decryptKeyblob(byte[] formattedKey, String passphrase) + throws InvalidSshKeyException { + try { + byte[] keyblob = getKeyBlob(formattedKey); + ByteArrayReader bar = new ByteArrayReader(keyblob); + String type = bar.readString(); + + if (type.equalsIgnoreCase("3des-cbc")) { + // Decrypt the key + byte[] keydata = makePassphraseKey(passphrase); + byte[] iv = new byte[8]; + + if (type.equals("3DES-CBC")) { + bar.read(iv); + } + + keyblob = bar.readBinaryString(); + + Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + KeySpec keyspec = new DESedeKeySpec(keydata); + Key key = SecretKeyFactory.getInstance("DESede").generateSecret(keyspec); + cipher.init(Cipher.DECRYPT_MODE, key, + new IvParameterSpec(iv, 0, cipher.getBlockSize())); + + ByteArrayReader data = new ByteArrayReader(cipher.doFinal( + keyblob)); + + if (data.readInt() == cookie) { + keyblob = data.readBinaryString(); + } else { + throw new InvalidSshKeyException( + "The host key is invalid, check the passphrase supplied"); + } + } else { + keyblob = bar.readBinaryString(); + } + + return keyblob; + } catch (Exception aoe) { + throw new InvalidSshKeyException("Failed to read host key"); + } + } + + /** + * + * + * @param keyblob + * @param passphrase + * + * @return + */ + public byte[] encryptKeyblob(byte[] keyblob, String passphrase) { + try { + ByteArrayWriter baw = new ByteArrayWriter(); + String type = "none"; + + if (passphrase != null) { + if (!passphrase.trim().equals("")) { + // Encrypt the data + type = "3DES-CBC"; + + // Decrypt the key + byte[] keydata = makePassphraseKey(passphrase); + byte[] iv = new byte[8]; + ConfigurationLoader.getRND().nextBytes(iv); + + Cipher cipher = Cipher.getInstance( + "DESede/CBC/PKCS5Padding"); + KeySpec keyspec = new DESedeKeySpec(keydata); + Key key = SecretKeyFactory.getInstance("DESede") + .generateSecret(keyspec); + cipher.init(Cipher.ENCRYPT_MODE, key, + new IvParameterSpec(iv, 0, cipher.getBlockSize())); + + ByteArrayWriter data = new ByteArrayWriter(); + baw.writeString(type); + baw.write(iv); + data.writeInt(cookie); + data.writeBinaryString(keyblob); + + // Encrypt and write + baw.writeBinaryString(cipher.doFinal(data.toByteArray())); + + return formatKey(baw.toByteArray()); + } + } + + // Write the type of encryption + baw.writeString(type); + + // Write the key blob + baw.writeBinaryString(keyblob); + + // Now set the keyblob to our new encrpyted (or not) blob + return formatKey(baw.toByteArray()); + } catch (Exception ioe) { + return null; + } + } + + /** + * + * + * @param algorithm + * + * @return + */ + public boolean supportsAlgorithm(String algorithm) { + return true; + } + + private byte[] makePassphraseKey(String passphrase) { + try { + // Generate the key using the passphrase + Hash md5 = new Hash("MD5"); + md5.putBytes(passphrase.getBytes()); + + byte[] key1 = md5.doFinal(); + md5.reset(); + md5.putBytes(passphrase.getBytes()); + md5.putBytes(key1); + + byte[] key2 = md5.doFinal(); + byte[] key = new byte[32]; + System.arraycopy(key1, 0, key, 0, 16); + System.arraycopy(key2, 0, key, 16, 16); + + return key; + } catch (NoSuchAlgorithmException nsae) { + return null; + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssKeyPair.java b/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssKeyPair.java new file mode 100644 index 0000000000000000000000000000000000000000..1362b71fdc9b8fb8d2f793dd981d83de4870e0c7 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssKeyPair.java @@ -0,0 +1,98 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey.dsa; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SshKeyPair; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.17 $ + */ +public class SshDssKeyPair extends SshKeyPair { + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public SshPrivateKey decodePrivateKey(byte[] encoded) + throws InvalidSshKeyException { + return new SshDssPrivateKey(encoded); + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public SshPublicKey decodePublicKey(byte[] encoded) + throws InvalidSshKeyException { + return new SshDssPublicKey(encoded); + } + + /** + * + * + * @param bits + */ + public void generate(int bits) { + try { + // Initialize the generator + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); + keyGen.initialize(bits, ConfigurationLoader.getRND()); + + KeyPair pair = keyGen.generateKeyPair(); + + // Get the keys + DSAPrivateKey prvKey = (DSAPrivateKey) pair.getPrivate(); + DSAPublicKey pubKey = (DSAPublicKey) pair.getPublic(); + + // Set the private key (the public is automatically generated) + setPrivateKey(new SshDssPrivateKey(prvKey)); + } catch (NoSuchAlgorithmException nsae) { + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssPrivateKey.java b/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssPrivateKey.java new file mode 100644 index 0000000000000000000000000000000000000000..a81a130034b860010e2666ae4589ff281571953e --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssPrivateKey.java @@ -0,0 +1,256 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey.dsa; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeySignatureException; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; +import com.sshtools.j2ssh.util.SimpleASNReader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.KeyFactory; +import java.security.Signature; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; + + +class SshDssPrivateKey extends SshPrivateKey { + private static Log log = LogFactory.getLog(SshDssPrivateKey.class); + DSAPrivateKey prvkey; + + /** + * Creates a new SshDssPrivateKey object. + * + * @param prvkey + */ + public SshDssPrivateKey(DSAPrivateKey prvkey) { + this.prvkey = prvkey; + } + + /** + * Creates a new SshDssPrivateKey object. + * + * @param key + * + * @throws InvalidSshKeyException + */ + public SshDssPrivateKey(byte[] key) throws InvalidSshKeyException { + try { + DSAPrivateKeySpec dsaKey; + + // Extract the key information + ByteArrayReader bar = new ByteArrayReader(key); + String header = bar.readString(); + + if (!header.equals(getAlgorithmName())) { + throw new InvalidSshKeyException(); + } + + BigInteger p = bar.readBigInteger(); + BigInteger q = bar.readBigInteger(); + BigInteger g = bar.readBigInteger(); + BigInteger x = bar.readBigInteger(); + dsaKey = new DSAPrivateKeySpec(x, p, q, g); + + KeyFactory kf = KeyFactory.getInstance("DSA"); + prvkey = (DSAPrivateKey) kf.generatePrivate(dsaKey); + } catch (Exception e) { + throw new InvalidSshKeyException(); + } + } + + /** + * + * + * @param obj + * + * @return + */ + public boolean equals(Object obj) { + if (obj instanceof SshDssPrivateKey) { + return prvkey.equals(((SshDssPrivateKey) obj).prvkey); + } + + return false; + } + + /** + * + * + * @return + */ + public int hashCode() { + return prvkey.hashCode(); + } + + /** + * + * + * @return + */ + public String getAlgorithmName() { + return "ssh-dss"; + } + + /** + * + * + * @return + */ + public int getBitLength() { + return prvkey.getX().bitLength(); + } + + /** + * + * + * @return + */ + public byte[] getEncoded() { + try { + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString("ssh-dss"); + baw.writeBigInteger(prvkey.getParams().getP()); + baw.writeBigInteger(prvkey.getParams().getQ()); + baw.writeBigInteger(prvkey.getParams().getG()); + baw.writeBigInteger(prvkey.getX()); + + return baw.toByteArray(); + } catch (IOException ioe) { + return null; + } + } + + /** + * + * + * @return + */ + public SshPublicKey getPublicKey() { + try { + DSAPublicKeySpec spec = new DSAPublicKeySpec(getY(), + prvkey.getParams().getP(), prvkey.getParams().getQ(), + prvkey.getParams().getG()); + KeyFactory kf = KeyFactory.getInstance("DSA"); + + return new SshDssPublicKey((DSAPublicKey) kf.generatePublic(spec)); + } catch (Exception e) { + return null; + } + } + + /** + * + * + * @param data + * + * @return + * + * @throws InvalidSshKeySignatureException + */ + public byte[] generateSignature(byte[] data) + throws InvalidSshKeySignatureException { + try { + Signature sig = Signature.getInstance("SHA1withDSA"); + sig.initSign(prvkey); + + /*java.util.Random rnd = new java.util.Random(); + byte[] buffer = new byte[20]; + rnd.nextBytes(buffer); + sig.update(buffer); + byte[] test = sig.sign();*/ + sig.update(data); + + byte[] signature = sig.sign(); + byte[] decoded = new byte[40]; + SimpleASNReader asn = new SimpleASNReader(signature); + asn.getByte(); + asn.getLength(); + asn.getByte(); + + byte[] r = asn.getData(); + asn.getByte(); + + byte[] s = asn.getData(); + + if (r.length >= 20) { + System.arraycopy(r, r.length - 20, decoded, 0, 20); + } else { + System.arraycopy(r, 0, decoded, 20 - r.length, r.length); + } + + if (s.length >= 20) { + System.arraycopy(s, s.length - 20, decoded, 20, 20); + } else { + System.arraycopy(s, 0, decoded, 20 + (20 - s.length), s.length); + } + + if (log.isDebugEnabled()) { + log.debug("s length is " + String.valueOf(s.length)); + log.debug("r length is " + String.valueOf(r.length)); + + String str = ""; + + for (int i = 0; i < signature.length; i++) { + str += (Integer.toHexString(signature[i] & 0xFF) + " "); + } + + log.debug("Java signature is " + str); + str = ""; + + for (int i = 0; i < decoded.length; i++) { + str += (Integer.toHexString(decoded[i] & 0xFF) + " "); + } + + log.debug("SSH signature is " + str); + } + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(getAlgorithmName()); + baw.writeBinaryString(decoded); + + return baw.toByteArray(); + } catch (Exception e) { + throw new InvalidSshKeySignatureException(e); + } + } + + private BigInteger getY() { + return prvkey.getParams().getG().modPow(prvkey.getX(), + prvkey.getParams().getP()); + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssPublicKey.java b/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssPublicKey.java new file mode 100644 index 0000000000000000000000000000000000000000..f8e86ccc7d639020c64a0b64264eb557a17f99d2 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/dsa/SshDssPublicKey.java @@ -0,0 +1,262 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey.dsa; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeySignatureException; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; +import com.sshtools.j2ssh.util.SimpleASNWriter; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPublicKeySpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.27 $ + */ +public class SshDssPublicKey extends SshPublicKey { + private static Log log = LogFactory.getLog(SshDssPublicKey.class); + private DSAPublicKey pubkey; + + /** + * Creates a new SshDssPublicKey object. + * + * @param key + */ + public SshDssPublicKey(DSAPublicKey key) { + this.pubkey = key; + } + + /** + * Creates a new SshDssPublicKey object. + * + * @param key + * + * @throws InvalidSshKeyException + */ + public SshDssPublicKey(byte[] key) throws InvalidSshKeyException { + try { + DSAPublicKeySpec dsaKey; + + // Extract the key information + ByteArrayReader bar = new ByteArrayReader(key); + String header = bar.readString(); + + if (!header.equals(getAlgorithmName())) { + throw new InvalidSshKeyException(); + } + + BigInteger p = bar.readBigInteger(); + BigInteger q = bar.readBigInteger(); + BigInteger g = bar.readBigInteger(); + BigInteger y = bar.readBigInteger(); + dsaKey = new DSAPublicKeySpec(y, p, q, g); + + KeyFactory kf = KeyFactory.getInstance("DSA"); + pubkey = (DSAPublicKey) kf.generatePublic(dsaKey); + } catch (Exception e) { + throw new InvalidSshKeyException(); + } + } + + /** + * + * + * @return + */ + public String getAlgorithmName() { + return "ssh-dss"; + } + + /** + * + * + * @return + */ + public int getBitLength() { + return pubkey.getY().bitLength(); + } + + /** + * + * + * @return + */ + public byte[] getEncoded() { + try { + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(getAlgorithmName()); + baw.writeBigInteger(pubkey.getParams().getP()); + baw.writeBigInteger(pubkey.getParams().getQ()); + baw.writeBigInteger(pubkey.getParams().getG()); + baw.writeBigInteger(pubkey.getY()); + + return baw.toByteArray(); + } catch (IOException ioe) { + return null; + } + } + + /** + * + * + * @param signature + * @param data + * + * @return + * + * @throws InvalidSshKeySignatureException + */ + public boolean verifySignature(byte[] signature, byte[] data) + throws InvalidSshKeySignatureException { + try { + // Check for differing version of the transport protocol + if (signature.length != 40) { + ByteArrayReader bar = new ByteArrayReader(signature); + byte[] sig = bar.readBinaryString(); + + //log.debug("Signature blob is " + new String(sig)); + String header = new String(sig); + log.debug("Header is " + header); + + if (!header.equals("ssh-dss")) { + throw new InvalidSshKeySignatureException(); + } + + signature = bar.readBinaryString(); + + //log.debug("Read signature from blob: " + new String(signature)); + } + + // Using a SimpleASNWriter + ByteArrayOutputStream r = new ByteArrayOutputStream(); + ByteArrayOutputStream s = new ByteArrayOutputStream(); + SimpleASNWriter asn = new SimpleASNWriter(); + asn.writeByte(0x02); + + if (((signature[0] & 0x80) == 0x80) && (signature[0] != 0x00)) { + r.write(0); + r.write(signature, 0, 20); + } else { + r.write(signature, 0, 20); + } + + asn.writeData(r.toByteArray()); + asn.writeByte(0x02); + + if (((signature[20] & 0x80) == 0x80) && (signature[20] != 0x00)) { + s.write(0); + s.write(signature, 20, 20); + } else { + s.write(signature, 20, 20); + } + + asn.writeData(s.toByteArray()); + + SimpleASNWriter asnEncoded = new SimpleASNWriter(); + asnEncoded.writeByte(0x30); + asnEncoded.writeData(asn.toByteArray()); + + byte[] encoded = asnEncoded.toByteArray(); + + if (log.isDebugEnabled()) { + log.debug("Verifying host key signature"); + log.debug("Signature length is " + + String.valueOf(signature.length)); + + String hex = ""; + + for (int i = 0; i < signature.length; i++) { + hex += (Integer.toHexString(signature[i] & 0xFF) + " "); + } + + log.debug("SSH: " + hex); + hex = ""; + + for (int i = 0; i < encoded.length; i++) { + hex += (Integer.toHexString(encoded[i] & 0xFF) + " "); + } + + log.debug("Encoded: " + hex); + } + + // The previous way + + /*byte[] encoded; + // Determine the encoded length of the big integers + int rlen = (((signature[0] & 0x80) == 0x80) ? 0x15 : 0x14); + log.debug("rlen=" + String.valueOf(rlen)); + int slen = (((signature[20] & 0x80) == 0x80) ? 0x15 : 0x14); + log.debug("slen=" + String.valueOf(slen)); + byte[] asn1r = { 0x30, (byte) (rlen + slen + 4), 0x02, (byte) rlen }; + byte[] asn1s = { 0x02, (byte) slen }; + // Create the encoded byte array + encoded = new byte[asn1r.length + rlen + asn1s.length + slen]; + // Copy the data and encode it into the array + System.arraycopy(asn1r, 0, encoded, 0, asn1r.length); + // Copy the integer inserting a zero byte if signed + int roffset = (((signature[0] & 0x80) == 0x80) ? 1 : 0); + System.arraycopy(signature, 0, encoded, asn1r.length + roffset, 20); + System.arraycopy(asn1s, 0, encoded, asn1r.length + roffset + 20, + asn1s.length); + int soffset = (((signature[20] & 0x80) == 0x80) ? 1 : 0); + System.arraycopy(signature, 20, encoded, + asn1r.length + roffset + 20 + asn1s.length + soffset, 20); + */ + Signature sig = Signature.getInstance("SHA1withDSA"); + sig.initVerify(pubkey); + sig.update(data); + + return sig.verify(encoded); + } catch (NoSuchAlgorithmException nsae) { + throw new InvalidSshKeySignatureException(); + } catch (InvalidKeyException ike) { + throw new InvalidSshKeySignatureException(); + } catch (IOException ioe) { + throw new InvalidSshKeySignatureException(); + } catch (SignatureException se) { + throw new InvalidSshKeySignatureException(); + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaKeyPair.java b/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaKeyPair.java new file mode 100644 index 0000000000000000000000000000000000000000..3df73ea81a7c3ad93de31094a58606d442b96d64 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaKeyPair.java @@ -0,0 +1,107 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey.rsa; + +import com.sshtools.j2ssh.configuration.ConfigurationLoader; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SshKeyPair; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class SshRsaKeyPair extends SshKeyPair { + private RSAPrivateKey prvKey; + private RSAPublicKey pubKey; + + /** + * Creates a new SshRsaKeyPair object. + */ + public SshRsaKeyPair() { + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public SshPrivateKey decodePrivateKey(byte[] encoded) + throws InvalidSshKeyException { + return new SshRsaPrivateKey(encoded); + } + + /** + * + * + * @param encoded + * + * @return + * + * @throws InvalidSshKeyException + */ + public SshPublicKey decodePublicKey(byte[] encoded) + throws InvalidSshKeyException { + return new SshRsaPublicKey(encoded); + } + + /** + * + * + * @param bits + */ + public void generate(int bits) { + try { + // Initialize the generator + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(bits, ConfigurationLoader.getRND()); + + KeyPair pair = keyGen.generateKeyPair(); + + // Get the keys and set + setPrivateKey(new SshRsaPrivateKey( + (RSAPrivateKey) pair.getPrivate(), + (RSAPublicKey) pair.getPublic())); + } catch (NoSuchAlgorithmException nsae) { + prvKey = null; + pubKey = null; + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaPrivateKey.java b/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaPrivateKey.java new file mode 100644 index 0000000000000000000000000000000000000000..3ac24a135eb7d04a248effaa2d3c85ae0976b220 --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaPrivateKey.java @@ -0,0 +1,195 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey.rsa; + +import com.sshtools.j2ssh.io.ByteArrayReader; +import com.sshtools.j2ssh.io.ByteArrayWriter; +import com.sshtools.j2ssh.transport.publickey.InvalidSshKeyException; +import com.sshtools.j2ssh.transport.publickey.SshPrivateKey; +import com.sshtools.j2ssh.transport.publickey.SshPublicKey; + +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.KeyFactory; +import java.security.Signature; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class SshRsaPrivateKey extends SshPrivateKey { + RSAPrivateKey prvKey; + RSAPublicKey pubKey; + + /** + * Creates a new SshRsaPrivateKey object. + * + * @param prv + * @param pub + */ + public SshRsaPrivateKey(RSAPrivateKey prv, RSAPublicKey pub) { + prvKey = prv; + pubKey = pub; + } + + /** + * Creates a new SshRsaPrivateKey object. + * + * @param encoded + * + * @throws InvalidSshKeyException + */ + public SshRsaPrivateKey(byte[] encoded) throws InvalidSshKeyException { + try { + // Extract the key information + ByteArrayReader bar = new ByteArrayReader(encoded); + + // Read the public key + String header = bar.readString(); + + if (!header.equals(getAlgorithmName())) { + throw new InvalidSshKeyException(); + } + + BigInteger e = bar.readBigInteger(); + BigInteger n = bar.readBigInteger(); + + // Read the private key + BigInteger p = bar.readBigInteger(); + RSAPrivateKeySpec prvSpec = new RSAPrivateKeySpec(n, p); + RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(n, e); + KeyFactory kf = KeyFactory.getInstance("RSA"); + prvKey = (RSAPrivateKey) kf.generatePrivate(prvSpec); + pubKey = (RSAPublicKey) kf.generatePublic(pubSpec); + } catch (Exception e) { + throw new InvalidSshKeyException(); + } + } + + /** + * + * + * @param obj + * + * @return + */ + public boolean equals(Object obj) { + if (obj instanceof SshRsaPrivateKey) { + return prvKey.equals(((SshRsaPrivateKey) obj).prvKey); + } + + return false; + } + + /** + * + * + * @return + */ + public int hashCode() { + return prvKey.hashCode(); + } + + /** + * + * + * @return + */ + public String getAlgorithmName() { + return "ssh-rsa"; + } + + /** + * + * + * @return + */ + public int getBitLength() { + return prvKey.getModulus().bitLength(); + } + + /** + * + * + * @return + */ + public byte[] getEncoded() { + try { + ByteArrayWriter baw = new ByteArrayWriter(); + + // The private key consists of the public key blob + baw.write(getPublicKey().getEncoded()); + + // And the private data + baw.writeBigInteger(prvKey.getPrivateExponent()); + + return baw.toByteArray(); + } catch (IOException ioe) { + return null; + } + } + + /** + * + * + * @return + */ + public SshPublicKey getPublicKey() { + return new SshRsaPublicKey(pubKey); + } + + /** + * + * + * @param data + * + * @return + */ + public byte[] generateSignature(byte[] data) { + try { + Signature sig = Signature.getInstance("SHA1withRSA"); + sig.initSign(prvKey); + sig.update(data); + + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(getAlgorithmName()); + baw.writeBinaryString(sig.sign()); + + return baw.toByteArray(); + } catch (Exception e) { + return null; + } + } +} diff --git a/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaPublicKey.java b/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaPublicKey.java new file mode 100644 index 0000000000000000000000000000000000000000..5eb7a302e1599dc35889b07cb86979d6216fdf9d --- /dev/null +++ b/src/com/sshtools/j2ssh/transport/publickey/rsa/SshRsaPublicKey.java @@ -0,0 +1,172 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.transport.publickey.rsa; + +import com.sshtools.j2ssh.io.*; +import com.sshtools.j2ssh.transport.publickey.*; + +import java.io.*; + +import java.math.*; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.19 $ + */ +public class SshRsaPublicKey extends SshPublicKey { + RSAPublicKey pubKey; + + /** + * Creates a new SshRsaPublicKey object. + * + * @param key + */ + public SshRsaPublicKey(RSAPublicKey key) { + pubKey = key; + } + + /** + * Creates a new SshRsaPublicKey object. + * + * @param encoded + * + * @throws InvalidSshKeyException + */ + public SshRsaPublicKey(byte[] encoded) throws InvalidSshKeyException { + try { + //this.hostKey = hostKey; + RSAPublicKeySpec rsaKey; + + // Extract the key information + ByteArrayReader bar = new ByteArrayReader(encoded); + String header = bar.readString(); + + if (!header.equals(getAlgorithmName())) { + throw new InvalidSshKeyException(); + } + + BigInteger e = bar.readBigInteger(); + BigInteger n = bar.readBigInteger(); + rsaKey = new RSAPublicKeySpec(n, e); + + try { + KeyFactory kf = KeyFactory.getInstance("RSA"); + pubKey = (RSAPublicKey) kf.generatePublic(rsaKey); + } catch (NoSuchAlgorithmException nsae) { + throw new InvalidSshKeyException(); + } catch (InvalidKeySpecException ikpe) { + throw new InvalidSshKeyException(); + } + } catch (IOException ioe) { + throw new InvalidSshKeyException(); + } + } + + /** + * + * + * @return + */ + public String getAlgorithmName() { + return "ssh-rsa"; + } + + /** + * + * + * @return + */ + public int getBitLength() { + return pubKey.getModulus().bitLength(); + } + + /** + * + * + * @return + */ + public byte[] getEncoded() { + try { + ByteArrayWriter baw = new ByteArrayWriter(); + baw.writeString(getAlgorithmName()); + baw.writeBigInteger(pubKey.getPublicExponent()); + baw.writeBigInteger(pubKey.getModulus()); + + return baw.toByteArray(); + } catch (IOException ioe) { + return null; + } + } + + /** + * + * + * @param signature + * @param data + * + * @return + * + * @throws InvalidSshKeySignatureException + */ + public boolean verifySignature(byte[] signature, byte[] data) + throws InvalidSshKeySignatureException { + try { + // Check for older versions of the transport protocol + if (signature.length != 128) { + ByteArrayReader bar = new ByteArrayReader(signature); + byte[] sig = bar.readBinaryString(); + String header = new String(sig); + + if (!header.equals(getAlgorithmName())) { + throw new InvalidSshKeySignatureException(); + } + + signature = bar.readBinaryString(); + } + + Signature s = Signature.getInstance("SHA1withRSA"); + s.initVerify(pubKey); + s.update(data); + + return s.verify(signature); + } catch (NoSuchAlgorithmException nsae) { + throw new InvalidSshKeySignatureException(); + } catch (IOException ioe) { + throw new InvalidSshKeySignatureException(); + } catch (InvalidKeyException ike) { + throw new InvalidSshKeySignatureException(); + } catch (SignatureException se) { + throw new InvalidSshKeySignatureException(); + } + } +} diff --git a/src/com/sshtools/j2ssh/util/Base64.java b/src/com/sshtools/j2ssh/util/Base64.java new file mode 100644 index 0000000000000000000000000000000000000000..51f4ece1a8bfdc5feecf0d34de8f2bf41812493d --- /dev/null +++ b/src/com/sshtools/j2ssh/util/Base64.java @@ -0,0 +1,693 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class Base64 { + /** */ + public final static boolean ENCODE = true; + + /** */ + public final static boolean DECODE = false; + private final static int MAX_LINE_LENGTH = 76; + private final static byte EQUALS_SIGN = (byte) '='; + private final static byte NEW_LINE = (byte) '\n'; + private final static byte[] ALPHABET = { + (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', + (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', + (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', + (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', + (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', + (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', + (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', + (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', + (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', + (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', + (byte) '8', (byte) '9', (byte) '+', (byte) '/' + }; + private final static byte[] DECODABET = { + -9, -9, -9, -9, -9, -9, -9, -9, -9, -5, -5, -9, -9, -5, -9, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -5, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, + // Decimal 33 - 42 + 62, -9, -9, -9, + // Decimal 44 - 46 + 63, + // Slash at decimal 47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -9, -9, -9, -1, -9, -9, -9, + // Decimal 62 - 64 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + // Letters 'A' through 'N' + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -9, -9, -9, -9, -9, -9, + + // Decimal 91 - 96 + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + // Letters 'a' through 'm' + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -9, -9, -9, -9 + }; + private final static byte BAD_ENCODING = -9; + + // Indicates error in encoding + private final static byte white_SPACE_ENC = -5; + + // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; + + // Indicates equals sign in encoding + private Base64() { + } + + /** + * + * + * @param s + * + * @return + */ + public static byte[] decode(String s) { + byte[] bytes = s.getBytes(); + + return decode(bytes, 0, bytes.length); + } + + // end decode + public static byte[] decode(byte[] source, int off, int len) { + int len34 = (len * 3) / 4; + byte[] outBuff = new byte[len34]; + + // Upper limit on size of output + int outBuffPosn = 0; + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + + for (i = 0; i < len; i++) { + sbiCrop = (byte) (source[i] & 0x7f); + + // Only the low seven bits + sbiDecode = DECODABET[sbiCrop]; + + if (sbiDecode >= white_SPACE_ENC) { + // White space, Equals sign or better + if (sbiDecode >= EQUALS_SIGN_ENC) { + b4[b4Posn++] = sbiCrop; + + if (b4Posn > 3) { + outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if (sbiCrop == EQUALS_SIGN) { + break; + } + } + + // end if: quartet built + } + + // end if: equals sign or better + } + // end if: white space, equals sign or better + else { + System.err.println("Bad Base64 input character at " + i + ": " + + source[i] + "(decimal)"); + + return null; + } + + // end else: + } + + // each input character + byte[] out = new byte[outBuffPosn]; + System.arraycopy(outBuff, 0, out, 0, outBuffPosn); + + return out; + } + + // end decode + public static Object decodeToObject(String encodedObject) { + byte[] objBytes = decode(encodedObject); + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + + try { + bais = new java.io.ByteArrayInputStream(objBytes); + ois = new java.io.ObjectInputStream(bais); + + return ois.readObject(); + } + // end try + catch (java.io.IOException e) { + e.printStackTrace(); + + return null; + } + // end catch + catch (java.lang.ClassNotFoundException e) { + e.printStackTrace(); + + return null; + } + // end catch + finally { + try { + bais.close(); + } catch (Exception e) { + } + + try { + ois.close(); + } catch (Exception e) { + } + } + + // end finally + } + + // end decodeObject + public static String decodeToString(String s) { + return new String(decode(s)); + } + + // end decodeToString + public static String encodeBytes(byte[] source, boolean ignoreMaxLineLength) { + return encodeBytes(source, 0, source.length, ignoreMaxLineLength); + } + + // end encodeBytes + public static String encodeBytes(byte[] source, int off, int len, + boolean ignoreMaxLineLength) { + int len43 = (len * 4) / 3; + byte[] outBuff = new byte[(len43) + (((len % 3) > 0) ? 4 : 0) + + (len43 / MAX_LINE_LENGTH)]; + + // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + + for (; d < len2; d += 3, e += 4) { + encode3to4(source, d + off, 3, outBuff, e); + lineLength += 4; + + if (!ignoreMaxLineLength) { + if (lineLength == MAX_LINE_LENGTH) { + outBuff[e + 4] = NEW_LINE; + e++; + lineLength = 0; + } + + // end if: end of line + } + } + + // en dfor: each piece of array + if (d < len) { + encode3to4(source, d + off, len - d, outBuff, e); + e += 4; + } + + // end if: some padding needed + return new String(outBuff, 0, e); + } + + // end encodeBytes + public static String encodeObject(java.io.Serializable serializableObject) { + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + + try { + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream(baos, Base64.ENCODE); + oos = new java.io.ObjectOutputStream(b64os); + oos.writeObject(serializableObject); + } + // end try + catch (java.io.IOException e) { + e.printStackTrace(); + + return null; + } + // end catch + finally { + try { + oos.close(); + } catch (Exception e) { + } + + try { + b64os.close(); + } catch (Exception e) { + } + + try { + baos.close(); + } catch (Exception e) { + } + } + + // end finally + return new String(baos.toByteArray()); + } + + // end encode + public static String encodeString(String s, boolean ignoreMaxLineLength) { + return encodeBytes(s.getBytes(), ignoreMaxLineLength); + } + + // end encodeString + public static void main(String[] args) { + String s = "P2/56wAAAgoAAAAmZGwtbW9kcHtzaWdue2RzYS1uaXN0LXNoYTF9LGRoe3Bs" + + "YWlufX0AAAAIM2Rlcy1jYmMAAAHIifTc7/X/swWj4OHVWX9RsUxWh4citAMwGzv6X9mUG6a" + + "mh5/2f6IiQ3lOeHFd5J0EAOeGNuLqE/RWJ/fFaZAzD6YTr1GZ5hflMzvRu3jbgZoLRz2TaT" + + "qeRs1yWrQoqANE2nBx6uDNrRahduqalLg2P/ezRCLGpqbw3HFgXmiZvzhd/rdEgZur7ZPnm" + + "EK7t4Ldypk/7xcK192JTbBXLDSKOEAqfYQb9CzW8MgEXde0DpMRZ9Fgm0KWPfz4CCJ0F9dd" + + "zcWl1nuGibL3klLKQANgecTurFlrxkBaHgxgl9nIvf24wH3nscvmD/uFOzacT/LzFaD03HFj" + + "/QHCiTezxVyyuJ39d3e6BBegV26vEFoGbrZ2mMf08C2MBmLmZELYdBRJ4kLpT5EZkzR8L4rT" + + "GxNiWkb4dGT42gHH41p2ad053lctyFWp/uQJnvJEiEm3BMURVY7k1S7zgv2FHgHE0LssXvBHx" + + "n/wnft0ne2NOqEXfs/Y4I39Nd7eDIupSVy/ZFfMmNPIhzKyC5lFMkjIMxPXNk548ZoP9Tnga" + + "4NPhHNKtcMinVvO2HT6dnIKMNb/NuXooULHIMVISpslRzXiVlTcN9vL/jhJhn9S"; + byte[] buf = Base64.decode(s); + System.out.println(new String(buf).toString()); + s = "P2/56wAAAgIAAAAmZGwtbW9kcHtzaWdue2RzYS1uaXN0LXNoYTF9LGRoe3BsYWlufX0A" + + "AAAEbm9uZQAAAcQAAAHAAAAAAAAABACwV9/fUska/ZHWa4YzXIPFJ4RbcFqPVV0vmyWkFekw" + + "wKE1mA0GVDRq9X0HTjA2DZWcQ46suVP/8mLwpnjTKNiRdFvXWGkxEpavLp+bjPa/NXsEsjZL" + + "aeO5iPZ11Xw5lx7uor8q/Ewwo9IcYOXzuOWN1EPCpdRv5OOaO3PCMq6QSQAAA/9j/IrSUDtf" + + "BLBlzPHBrzboKjIMXv9O8CIRtSqnV7GV9wllgh3Cm+Eh+rd5CB698JGQD9tMdBn4s8bj/BDL" + + "4qsxbQbAsZOIin6fqDKNLDxFo357eXM06I5569PgC6cuBoJXOyQTg+sLrjT8/b3/1N4TjdZN" + + "JiKiSiuOzkn03tNSbgAAAKDJhI2ZNNvzOXhp+VFuoY//D9GvHwAAA/9NkAROm4wF7NCPsBXd" + + "/+QfNV3NM/FSpOonZZDg2AVnCCGdLOXCWEj60EVHWEf5FbOjJ1KynbbdZA6q5JtVDIYuU9wH" + + "BCsT5iCexGD5j2HYNcUXT4VG5a6qzqloR2JizlZOcjiEM2j0/hydFUei0VYmJNY5L//AprO6" + + "1UJL2OGFEQAAAJ0Ts+KlcAYmJjJWODOG3mYuiTgv7A=="; + buf = Base64.decode(s); + System.out.println(new String(buf).toString()); + buf = Base64.decode( + "P2/56wAAAi4AAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS1wa2NzMXYyLW9hZXB9fQAAAARub25lAAAB3wAAAdsAAAARAQABAAAD/iJ48YNIqLobasqkyRAD6Ejzhe0bK0Nd12iq0X9xG7M5xyVJns5SH3oAPwsa/V63omsQnm/ERG5lCnFGymTSCTpz0jGYLAh81S4XGbZEJRltP75LiM4J1OIfQkF7Zxd/mYFAYpu50fOLTrk+EwOCyQJK63uXzxQHCU1JKzt61m05AAAEALhvA6F1Ffhf/HLPKe3mp/CdTYQyioHzdL2ur6jyvh+b5wb8WuiaL+xu08vA7/Q763M/TXLX3jMWKOfV3HFn656hBCjnePwXp+uJNIQ4+oxg5H7nr8yo2Tc3Umt9fzgajoLDSd488iozmlSgKeRoVy7hKAGuveGtFqqruNAYArNfAAACALXcpb2stcqNdyTGUPIK/uUBkEeEJGgomqFPZbkMNHqZqEPLa7cJdHIl6wiol3ziQKvvUm/8ya4y7tR9Mzay/cIAAAIAwU2/rquz6oQ1GVJVRsO47Ibes2Hcl8tZRC9cBDy5vIPhzPhsD3pxxXnc1gEUybWqkuO6q1XilE/qN/eAKFSDuQAAAgD0QMX768Ucuv2Eu/ZVkebKBBV7jo4seZyd+hKloFotU4mReU7kNq+oYG19pL07n1TN4SodVoykXPSLBowCKCvX"); + System.out.println(new String(buf).toString()); + buf = Base64.decode( + "P2/56wAAAjsAAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS1wa2NzMXYyLW9hZXB9fQAAAAgzZGVzLWNiYwAAAegYJSJacx5dZo5rvtyJEp5qFyBXDOkcGH/H4/dJuny1cWnP5eXOaYt1hwc6ZEUIq4bUISGuXSzmRb+mpXZdkAPPt2RLhy66FnwnERnbItyWsNHrMxT5/oug/TW1l+rh0m/46edQhkla+qpgt3ZCJfBRzwihKAAeQJIt18e7XmvVT5g14Xu5fulXPfKT/cPu6Ox1pwRrOTv2ooM8alM2+K+5uCaP9C3qhEhFcyZOsKoigJt8oIZJD7TBrb2adVfzjyNWXZLw5Lq+liWmGTePvf9Mkx+MgFAyIOT4gV391+Rit8ZjSQaJ5jtsSaqw/MgqtTCWz6aXAaLnxP579a+tVubfVQrGLAa6ztGjI/0DmzEH+OvOLfXljeaEPKXhOxTf2O7Pwn8MDBStJHPXPLZZnsoUyTCajnzxw/ohqxOtgE9nqqO1QFVF6Cd74yZlhQSScRKkBcUlqcenxtruEOvvZXgAc8T5UtfvF8AooI22zltyKZDFJx3vJD6TEoFQSq4zu8H4Eipr42HPpUvIFuVAJFlZepI/RVirsU6sDjh8do0vj9ZGdhdBaD8kR7lrPHAJkmROHljJhEI97YWUJZNXS9i63gVvplsi9/x6uEWjn8eNu08IXID82X+LbvEdmTWOhuaSIqyNjyVe7g=="); + System.out.println(new String(buf).toString()); + } + + /* + * ******** D E C O D I N G M E T H O D S ******** + */ + private static byte[] decode4to3(byte[] fourBytes) { + byte[] outBuff1 = new byte[3]; + int count = decode4to3(fourBytes, 0, outBuff1, 0); + byte[] outBuff2 = new byte[count]; + + for (int i = 0; i < count; i++) { + outBuff2[i] = outBuff1[i]; + } + + return outBuff2; + } + + private static int decode4to3(byte[] source, int srcOffset, + byte[] destination, int destOffset) { + // Example: Dk== + if (source[srcOffset + 2] == EQUALS_SIGN) { + int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6) | + ((DECODABET[source[srcOffset + 1]] << 24) >>> 12); + destination[destOffset] = (byte) (outBuff >>> 16); + + return 1; + } + // Example: DkL= + else if (source[srcOffset + 3] == EQUALS_SIGN) { + int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6) | + ((DECODABET[source[srcOffset + 1]] << 24) >>> 12) | + ((DECODABET[source[srcOffset + 2]] << 24) >>> 18); + destination[destOffset] = (byte) (outBuff >>> 16); + destination[destOffset + 1] = (byte) (outBuff >>> 8); + + return 2; + } + // Example: DkLE + else { + int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6) | + ((DECODABET[source[srcOffset + 1]] << 24) >>> 12) | + ((DECODABET[source[srcOffset + 2]] << 24) >>> 18) | + ((DECODABET[source[srcOffset + 3]] << 24) >>> 24); + destination[destOffset] = (byte) (outBuff >> 16); + destination[destOffset + 1] = (byte) (outBuff >> 8); + destination[destOffset + 2] = (byte) (outBuff); + + return 3; + } + } + + // end decodeToBytes + + /* + * ******** E N C O D I N G M E T H O D S ******** + */ + private static byte[] encode3to4(byte[] threeBytes) { + return encode3to4(threeBytes, 3); + } + + // end encodeToBytes + private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) { + byte[] dest = new byte[4]; + encode3to4(threeBytes, 0, numSigBytes, dest, 0); + + return dest; + } + + private static byte[] encode3to4(byte[] source, int srcOffset, + int numSigBytes, byte[] destination, int destOffset) { + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ((numSigBytes > 0) ? ((source[srcOffset] << 24) >>> 8) : 0) | + ((numSigBytes > 1) ? ((source[srcOffset + 1] << 24) >>> 16) : 0) | + ((numSigBytes > 2) ? ((source[srcOffset + 2] << 24) >>> 24) : 0); + + switch (numSigBytes) { + case 3: + destination[destOffset] = ALPHABET[(inBuff >>> 18)]; + destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; + destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; + destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f]; + + return destination; + + case 2: + destination[destOffset] = ALPHABET[(inBuff >>> 18)]; + destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; + destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; + destination[destOffset + 3] = EQUALS_SIGN; + + return destination; + + case 1: + destination[destOffset] = ALPHABET[(inBuff >>> 18)]; + destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; + destination[destOffset + 2] = EQUALS_SIGN; + destination[destOffset + 3] = EQUALS_SIGN; + + return destination; + + default: + return destination; + } + + // end switch + } + + // end encode3to4 + + /* + * ******** I N N E R C L A S S I N P U T S T R E A M ******** + */ + public static class InputStream extends java.io.FilterInputStream { + private byte[] buffer; + + // Small buffer holding converted data + private boolean encode; + + // Encoding or decoding + private int bufferLength; + + // Length of buffer (3 or 4) + private int numSigBytes; + + // Number of meaningful bytes in the buffer + private int position; + + // Current position in the buffer + public InputStream(java.io.InputStream in) { + this(in, Base64.DECODE); + } + + // end constructor + public InputStream(java.io.InputStream in, boolean encode) { + super(in); + this.encode = encode; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[bufferLength]; + this.position = -1; + } + + // end constructor + public int read() throws java.io.IOException { + // Do we need to get data? + if (position < 0) { + if (encode) { + byte[] b3 = new byte[3]; + numSigBytes = 0; + + for (int i = 0; i < 3; i++) { + try { + int b = in.read(); + + // If end of stream, b is -1. + if (b >= 0) { + b3[i] = (byte) b; + numSigBytes++; + } + + // end if: not end of stream + } + // end try: read + catch (java.io.IOException e) { + // Only a problem if we got no data at all. + if (i == 0) { + throw e; + } + } + + // end catch + } + + // end for: each needed input byte + if (numSigBytes > 0) { + encode3to4(b3, 0, numSigBytes, buffer, 0); + position = 0; + } + + // end if: got data + } + // end if: encoding + // Else decoding + else { + byte[] b4 = new byte[4]; + int i = 0; + + for (i = 0; i < 4; i++) { + int b = 0; + + do { + b = in.read(); + } while ((b >= 0) && + (DECODABET[b & 0x7f] < white_SPACE_ENC)); + + if (b < 0) { + break; + + // Reads a -1 if end of stream + } + + b4[i] = (byte) b; + } + + // end for: each needed input byte + if (i == 4) { + numSigBytes = decode4to3(b4, 0, buffer, 0); + position = 0; + } + + // end if: got four characters + } + + // end else: decode + } + + // end else: get data + // Got data? + if (position >= 0) { + // End of relevant data? + if (!encode && (position >= numSigBytes)) { + return -1; + } + + int b = buffer[position++]; + + if (position >= bufferLength) { + position = -1; + } + + return b; + } + // end if: position >= 0 + // Else error + else { + return -1; + } + } + + // end read + public int read(byte[] dest, int off, int len) + throws java.io.IOException { + int i; + int b; + + for (i = 0; i < len; i++) { + b = read(); + + if (b < 0) { + return -1; + } + + dest[off + i] = (byte) b; + } + + // end for: each byte read + return i; + } + + // end read + } + + // end inner class InputStream + + /* + * ******** I N N E R C L A S S O U T P U T S T R E A M ******** + */ + public static class OutputStream extends java.io.FilterOutputStream { + private byte[] buffer; + private boolean encode; + private int bufferLength; + private int lineLength; + private int position; + + public OutputStream(java.io.OutputStream out) { + this(out, Base64.ENCODE); + } + + // end constructor + public OutputStream(java.io.OutputStream out, boolean encode) { + super(out); + this.encode = encode; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[bufferLength]; + this.position = 0; + this.lineLength = 0; + } + + // end constructor + public void close() throws java.io.IOException { + this.flush(); + super.close(); + out.close(); + buffer = null; + out = null; + } + + // end close + public void flush() throws java.io.IOException { + if (position > 0) { + if (encode) { + out.write(Base64.encode3to4(buffer, position)); + } + // end if: encoding + else { + throw new java.io.IOException( + "Base64 input not properly padded."); + } + + // end else: decoding + } + + // end if: buffer partially full + super.flush(); + out.flush(); + } + + // end flush + public void write(int theByte) throws java.io.IOException { + buffer[position++] = (byte) theByte; + + if (position >= bufferLength) { + if (encode) { + out.write(Base64.encode3to4(buffer, bufferLength)); + lineLength += 4; + + if (lineLength >= MAX_LINE_LENGTH) { + out.write(NEW_LINE); + lineLength = 0; + } + + // end if: end o fline + } + // end if: encoding + else { + out.write(Base64.decode4to3(buffer)); + } + + position = 0; + } + + // end if: convert and flush + } + + // end write + public void write(byte[] theBytes, int off, int len) + throws java.io.IOException { + for (int i = 0; i < len; i++) { + write(theBytes[off + i]); + } + + // end for: each byte written + } + + // end write + } + + // end inner class OutputStream +} + + +// end class Base64 diff --git a/src/com/sshtools/j2ssh/util/DynamicClassLoader.java b/src/com/sshtools/j2ssh/util/DynamicClassLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..0a0580da04a0f51107d07f320399263e47138cc3 --- /dev/null +++ b/src/com/sshtools/j2ssh/util/DynamicClassLoader.java @@ -0,0 +1,533 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import java.net.URL; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class DynamicClassLoader extends ClassLoader { + private static Log log = LogFactory.getLog(DynamicClassLoader.class); + private static int generationCounter = 0; + private Hashtable cache; + private List classpath = new Vector(); + private int generation; + private ClassLoader parent; + + /** + * Creates a new DynamicClassLoader object. + * + * @param parent + * @param classpath + * + * @throws IllegalArgumentException + */ + public DynamicClassLoader(ClassLoader parent, List classpath) + throws IllegalArgumentException { + this.parent = parent; + + // Create the cache to hold the loaded classes + cache = new Hashtable(); + + Iterator it = classpath.iterator(); + + while (it.hasNext()) { + Object obj = it.next(); + File f = null; + + if (obj instanceof String) { + f = new File((String) obj); + } else if (obj instanceof File) { + f = (File) obj; + } else { + throw new IllegalArgumentException( + "Entries in classpath must be either a String or File object"); + } + + if (!f.exists()) { + throw new IllegalArgumentException("Classpath " + + f.getAbsolutePath() + " doesn't exist!"); + } else if (!f.canRead()) { + throw new IllegalArgumentException( + "Don't have read access for file " + f.getAbsolutePath()); + } + + // Check that it is a directory or jar file + if (!(f.isDirectory() || isJarArchive(f))) { + throw new IllegalArgumentException(f.getAbsolutePath() + + " is not a directory or jar file" + + " or if it's a jar file then it is corrupted."); + } + + this.classpath.add(f); + } + + // Increment and store generation counter + this.generation = generationCounter++; + } + + /** + * + * + * @param name + * + * @return + */ + public URL getResource(String name) { + URL u = getSystemResource(name); + + if (u != null) { + return u; + } + + // Load for it only in directories since no URL can point into + // a zip file. + Iterator it = classpath.iterator(); + + while (it.hasNext()) { + File file = (File) it.next(); + + if (file.isDirectory()) { + String fileName = name.replace('/', File.separatorChar); + File resFile = new File(file, fileName); + + if (resFile.exists()) { + // Build a file:// URL form the file name + try { + return new URL("file://" + resFile.getAbsolutePath()); + } catch (java.net.MalformedURLException badurl) { + badurl.printStackTrace(); + + return null; + } + } + } + } + + // Not found + return null; + } + + /** + * + * + * @param name + * + * @return + */ + public InputStream getResourceAsStream(String name) { + // Try to load it from the system class + InputStream s = getSystemResourceAsStream(name); + + if (s == null) { + // Try to find it from every classpath + Iterator it = classpath.iterator(); + + while (it.hasNext()) { + File file = (File) it.next(); + + if (file.isDirectory()) { + s = loadResourceFromDirectory(file, name); + } else { + s = loadResourceFromZipfile(file, name); + } + + if (s != null) { + break; + } + } + } + + return s; + } + + /** + * + * + * @return + */ + public DynamicClassLoader reinstantiate() { + return new DynamicClassLoader(parent, classpath); + } + + /** + * + * + * @param classname + * + * @return + */ + public synchronized boolean shouldReload(String classname) { + ClassCacheEntry entry = (ClassCacheEntry) cache.get(classname); + + if (entry == null) { + // class wasn't even loaded + return false; + } else if (entry.isSystemClass()) { + // System classes cannot be reloaded + return false; + } else { + boolean reload = (entry.origin.lastModified() != entry.lastModified); + + return reload; + } + } + + /** + * + * + * @return + */ + public synchronized boolean shouldReload() { + // Check whether any class has changed + Enumeration e = cache.elements(); + + while (e.hasMoreElements()) { + ClassCacheEntry entry = (ClassCacheEntry) e.nextElement(); + + if (entry.isSystemClass()) { + continue; + } + + long msOrigin = entry.origin.lastModified(); + + if (msOrigin == 0) { + // class no longer exists + return true; + } + + if (msOrigin != entry.lastModified) { + // class is modified + return true; + } + } + + // No changes, no need to reload + return false; + } + + /** + * + * + * @param name + * @param resolve + * + * @return + * + * @throws ClassNotFoundException + */ + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + // The class object that will be returned. + Class c = null; + + // Use the cached value, if this class is already loaded into + // this classloader. + ClassCacheEntry entry = (ClassCacheEntry) cache.get(name); + + if (entry != null) { + // Class found in our cache + c = entry.loadedClass; + + if (resolve) { + resolveClass(c); + } + + return c; + } + + if (!securityAllowsClass(name)) { + return loadSystemClass(name, resolve); + } + + // Attempt to load the class from the system + try { + c = loadSystemClass(name, resolve); + + if (c != null) { + if (resolve) { + resolveClass(c); + } + + return c; + } + } catch (Exception e) { + c = null; + } + + // Try to load it from each classpath + Iterator it = classpath.iterator(); + + // Cache entry. + ClassCacheEntry classCache = new ClassCacheEntry(); + + while (it.hasNext()) { + byte[] classData; + File file = (File) it.next(); + + try { + if (file.isDirectory()) { + classData = loadClassFromDirectory(file, name, classCache); + } else { + classData = loadClassFromZipfile(file, name, classCache); + } + } catch (IOException ioe) { + // Error while reading in data, consider it as not found + classData = null; + } + + if (classData != null) { + // Define the class + c = defineClass(name, classData, 0, classData.length); + + // Cache the result; + classCache.loadedClass = c; + + // Origin is set by the specific loader + classCache.lastModified = classCache.origin.lastModified(); + cache.put(name, classCache); + + // Resolve it if necessary + if (resolve) { + resolveClass(c); + } + + return c; + } + } + + // If not found in any classpath + throw new ClassNotFoundException(name); + } + + private boolean isJarArchive(File file) { + boolean isArchive = true; + ZipFile zipFile = null; + + try { + zipFile = new ZipFile(file); + } catch (ZipException zipCurrupted) { + isArchive = false; + } catch (IOException anyIOError) { + isArchive = false; + } finally { + if (zipFile != null) { + try { + zipFile.close(); + } catch (IOException ignored) { + } + } + } + + return isArchive; + } + + private byte[] loadBytesFromStream(InputStream in, int length) + throws IOException { + byte[] buf = new byte[length]; + int nRead; + int count = 0; + + while ((length > 0) && ((nRead = in.read(buf, count, length)) != -1)) { + count += nRead; + length -= nRead; + } + + return buf; + } + + private byte[] loadClassFromDirectory(File dir, String name, + ClassCacheEntry cache) throws IOException { + // Translate class name to file name + String classFileName = name.replace('.', File.separatorChar) + + ".class"; + + // Check for garbage input at beginning of file name + // i.e. ../ or similar + if (!Character.isJavaIdentifierStart(classFileName.charAt(0))) { + // Find real beginning of class name + int start = 1; + + while (!Character.isJavaIdentifierStart(classFileName.charAt( + start++))) { + ; + } + + classFileName = classFileName.substring(start); + } + + File classFile = new File(dir, classFileName); + + if (classFile.exists()) { + cache.origin = classFile; + + InputStream in = new FileInputStream(classFile); + + try { + return loadBytesFromStream(in, (int) classFile.length()); + } finally { + in.close(); + } + } else { + // Not found + return null; + } + } + + private byte[] loadClassFromZipfile(File file, String name, + ClassCacheEntry cache) throws IOException { + // Translate class name to file name + String classFileName = name.replace('.', '/') + ".class"; + ZipFile zipfile = new ZipFile(file); + + try { + ZipEntry entry = zipfile.getEntry(classFileName); + + if (entry != null) { + cache.origin = file; + + return loadBytesFromStream(zipfile.getInputStream(entry), + (int) entry.getSize()); + } else { + // Not found + return null; + } + } finally { + zipfile.close(); + } + } + + private InputStream loadResourceFromDirectory(File dir, String name) { + // Name of resources are always separated by / + String fileName = name.replace('/', File.separatorChar); + File resFile = new File(dir, fileName); + + if (resFile.exists()) { + try { + return new FileInputStream(resFile); + } catch (FileNotFoundException shouldnothappen) { + return null; + } + } else { + return null; + } + } + + private InputStream loadResourceFromZipfile(File file, String name) { + try { + ZipFile zipfile = new ZipFile(file); + ZipEntry entry = zipfile.getEntry(name); + + if (entry != null) { + return zipfile.getInputStream(entry); + } else { + return null; + } + } catch (IOException e) { + return null; + } + } + + private Class loadSystemClass(String name, boolean resolve) + throws NoClassDefFoundError, ClassNotFoundException { + // Class c = findSystemClass(name); + Class c = parent.loadClass(name); + + if (resolve) { + resolveClass(c); + } + + // Throws if not found. + // Add cache entry + ClassCacheEntry cacheEntry = new ClassCacheEntry(); + cacheEntry.origin = null; + cacheEntry.loadedClass = c; + cacheEntry.lastModified = Long.MAX_VALUE; + cache.put(name, cacheEntry); + + if (resolve) { + resolveClass(c); + } + + return c; + } + + private boolean securityAllowsClass(String className) { + try { + SecurityManager security = System.getSecurityManager(); + + if (security == null) { + // if there's no security manager then all classes + // are allowed to be loaded + return true; + } + + int lastDot = className.lastIndexOf('.'); + + // Check if we are allowed to load the class' package + security.checkPackageDefinition((lastDot > -1) + ? className.substring(0, lastDot) : ""); + + // Throws if not allowed + return true; + } catch (SecurityException e) { + return false; + } + } + + private static class ClassCacheEntry { + Class loadedClass; + File origin; + long lastModified; + + public boolean isSystemClass() { + return origin == null; + } + } +} diff --git a/src/com/sshtools/j2ssh/util/ExtensionClassLoader.java b/src/com/sshtools/j2ssh/util/ExtensionClassLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..e29bfbb66a256cb2f61cdd2ddace79643dba5396 --- /dev/null +++ b/src/com/sshtools/j2ssh/util/ExtensionClassLoader.java @@ -0,0 +1,476 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Set; +import java.util.Vector; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + + +/** + * <p>Title: </p> + * <p>Description: </p> + * <p>Copyright: Copyright (c) 2003</p> + * <p>Company: </p> + * @author Lee David Painter + * @version $Id: ExtensionClassLoader.java,v 1.11 2003/09/11 15:35:16 martianx Exp $ + */ +public class ExtensionClassLoader extends ClassLoader { + private static Log log = LogFactory.getLog(ExtensionClassLoader.class); + Vector classpath = new Vector(); + private Hashtable cache = new Hashtable(); + private HashMap packages = new HashMap(); + + public ExtensionClassLoader() { + } + + public ExtensionClassLoader(ClassLoader parent) { + super(parent); + } + + public void add(String file) { + add(new File(file)); + } + + public void add(File[] files) { + for (int i = 0; i < files.length; i++) { + add(files[i]); + } + } + + public void add(File file) { + if (!file.exists()) { + throw new IllegalArgumentException("Classpath " + + file.getAbsolutePath() + " doesn't exist!"); + } else if (!file.canRead()) { + throw new IllegalArgumentException( + "Don't have read access for file " + file.getAbsolutePath()); + } + + // Check that it is a directory or jar file + if (!(file.isDirectory() || isJarArchive(file))) { + throw new IllegalArgumentException(file.getAbsolutePath() + + " is not a directory or jar file" + + " or if it's a jar file then it is corrupted."); + } + + log.info("Adding " + file.getAbsolutePath() + + " to the extension classpath"); + this.classpath.add(file); + } + + public boolean isJarArchive(File file) { + boolean isArchive = true; + ZipFile zipFile = null; + + try { + zipFile = new ZipFile(file); + } catch (ZipException zipCurrupted) { + isArchive = false; + } catch (IOException anyIOError) { + isArchive = false; + } finally { + if (zipFile != null) { + try { + zipFile.close(); + } catch (IOException ignored) { + } + } + } + + return isArchive; + } + + public URL getResource(String name, File location) { + if (isJarArchive(location)) { + return findResourceInZipfile(location, name); + } else { + return findResourceInDirectory(location, name); + } + } + + protected URL findResource(String name) { + // The class object that will be returned. + URL url = null; + + // Try to load it from each classpath + Iterator it = classpath.iterator(); + + while (it.hasNext()) { + byte[] classData; + File file = (File) it.next(); + + if (file.isDirectory()) { + url = findResourceInDirectory(file, name); + } else { + url = findResourceInZipfile(file, name); + } + + if (url != null) { + if (log.isDebugEnabled()) { + log.debug("Found resource " + url.toExternalForm()); + } + + return url; + } + + if (log.isDebugEnabled()) { + log.debug("Could not find resource " + name); + } + } + + return null; + } + + protected Enumeration findResources(String name) { + HashSet resources = new HashSet(); + URL url = null; + + // Try to load it from each classpath + Iterator it = classpath.iterator(); + + while (it.hasNext()) { + byte[] classData; + File file = (File) it.next(); + + if (file.isDirectory()) { + url = findResourceInDirectory(file, name); + } else { + url = findResourceInZipfile(file, name); + } + + if (url != null) { + if (log.isDebugEnabled()) { + log.debug("Found resource " + url.toExternalForm()); + } + + resources.add(url); + } + } + + return new ResourceEnumeration(resources); + } + + public byte[] loadClassData(String name) throws ClassNotFoundException { + // Try to load it from each classpath + Iterator it = classpath.iterator(); + + // Cache entry. + ClassCacheEntry classCache = new ClassCacheEntry(); + + while (it.hasNext()) { + byte[] classData; + File file = (File) it.next(); + + try { + if (file.isDirectory()) { + classData = loadClassFromDirectory(file, name, null); + } else { + classData = loadClassFromZipfile(file, name, null); + } + } catch (IOException ioe) { + // Error while reading in data, consider it as not found + classData = null; + } + + if (classData != null) { + return classData; + } + } + + // If not found in any classpath + throw new ClassNotFoundException(name); + } + + public Class findClass(String name) throws ClassNotFoundException { + // The class object that will be returned. + Class c = null; + + // Use the cached value, if this class is already loaded into + // this classloader. + ClassCacheEntry entry = (ClassCacheEntry) cache.get(name); + + if (entry != null) { + if (log.isDebugEnabled()) { + log.debug("Loaded " + name + " from cache"); + } + + // Class found in our cache + c = entry.loadedClass; + resolveClass(c); + + return c; + } + + // Try to load it from each classpath + Iterator it = classpath.iterator(); + + // Cache entry. + ClassCacheEntry classCache = new ClassCacheEntry(); + + while (it.hasNext()) { + byte[] classData; + File file = (File) it.next(); + + try { + if (file.isDirectory()) { + classData = loadClassFromDirectory(file, name, classCache); + } else { + classData = loadClassFromZipfile(file, name, classCache); + } + } catch (IOException ioe) { + // Error while reading in data, consider it as not found + classData = null; + } + + if (classData != null) { + // Does the package exist? + String packageName = ""; + + if (name.lastIndexOf(".") > 0) { + packageName = name.substring(0, name.lastIndexOf(".")); + } + + if (!packageName.equals("") && + !packages.containsKey(packageName)) { + packages.put(packageName, + definePackage(packageName, "", "", "", "", "", "", null)); + + // Define the class + } + + c = defineClass(name, classData, 0, classData.length); + + // Cache the result; + classCache.loadedClass = c; + + // Origin is set by the specific loader + classCache.lastModified = classCache.origin.lastModified(); + cache.put(name, classCache); + resolveClass(c); + + if (log.isDebugEnabled()) { + log.debug("Loaded " + name + + " adding to cache and returning"); + } + + return c; + } + } + + // If not found in any classpath + throw new ClassNotFoundException(name); + } + + private byte[] loadBytesFromStream(InputStream in, int length) + throws IOException { + byte[] buf = new byte[length]; + int nRead; + int count = 0; + + while ((length > 0) && ((nRead = in.read(buf, count, length)) != -1)) { + count += nRead; + length -= nRead; + } + + return buf; + } + + private byte[] loadClassFromDirectory(File dir, String name, + ClassCacheEntry cache) throws IOException { + // Translate class name to file name + String classFileName = name.replace('.', File.separatorChar) + + ".class"; + + // Check for garbage input at beginning of file name + // i.e. ../ or similar + if (!Character.isJavaIdentifierStart(classFileName.charAt(0))) { + // Find real beginning of class name + int start = 1; + + while (!Character.isJavaIdentifierStart(classFileName.charAt( + start++))) { + ; + } + + classFileName = classFileName.substring(start); + } + + File classFile = new File(dir, classFileName); + + if (classFile.exists()) { + if (cache != null) { + cache.origin = classFile; + } + + InputStream in = new FileInputStream(classFile); + + try { + return loadBytesFromStream(in, (int) classFile.length()); + } finally { + in.close(); + } + } else { + // Not found + return null; + } + } + + private byte[] loadClassFromZipfile(File file, String name, + ClassCacheEntry cache) throws IOException { + // Translate class name to file name + String classFileName = name.replace('.', '/') + ".class"; + ZipFile zipfile = new ZipFile(file); + + try { + ZipEntry entry = zipfile.getEntry(classFileName); + + if (entry != null) { + if (cache != null) { + cache.origin = file; + } + + return loadBytesFromStream(zipfile.getInputStream(entry), + (int) entry.getSize()); + } else { + // Not found + return null; + } + } finally { + zipfile.close(); + } + } + + private InputStream loadResourceFromDirectory(File dir, String name) { + // Name of resources are always separated by / + String fileName = name.replace('/', File.separatorChar); + File resFile = new File(dir, fileName); + + if (resFile.exists()) { + try { + return new FileInputStream(resFile); + } catch (FileNotFoundException shouldnothappen) { + return null; + } + } else { + return null; + } + } + + private URL findResourceInDirectory(File dir, String name) { + // Name of resources are always separated by / + String fileName = name.replace('/', File.separatorChar); + File resFile = new File(dir, fileName); + + if (resFile.exists()) { + try { + return resFile.toURL(); + } catch (MalformedURLException ex) { + return null; + } + } else { + return null; + } + } + + private URL findResourceInZipfile(File file, String name) { + try { + ZipFile zipfile = new ZipFile(file); + ZipEntry entry = zipfile.getEntry(name); + + if (entry != null) { + return new URL("jar:" + file.toURL() + "!" + + (name.startsWith("/") ? "" : "/") + name); + } else { + return null; + } + } catch (IOException e) { + return null; + } + } + + private InputStream loadResourceFromZipfile(File file, String name) { + try { + ZipFile zipfile = new ZipFile(file); + ZipEntry entry = zipfile.getEntry(name); + + if (entry != null) { + return zipfile.getInputStream(entry); + } else { + return null; + } + } catch (IOException e) { + return null; + } + } + + private class ResourceEnumeration implements Enumeration { + Set resources; + Iterator it; + + ResourceEnumeration(Set resources) { + this.resources = resources; + it = resources.iterator(); + } + + public boolean hasMoreElements() { + return it.hasNext(); + } + + public Object nextElement() { + return it.next(); + } + } + + private static class ClassCacheEntry { + Class loadedClass; + File origin; + long lastModified; + + public boolean isSystemClass() { + return origin == null; + } + } +} diff --git a/src/com/sshtools/j2ssh/util/Hash.java b/src/com/sshtools/j2ssh/util/Hash.java new file mode 100644 index 0000000000000000000000000000000000000000..d69746916fd144a0b143eafaa95b8bfb74e060c3 --- /dev/null +++ b/src/com/sshtools/j2ssh/util/Hash.java @@ -0,0 +1,145 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + +import com.sshtools.j2ssh.io.*; + +import java.io.*; + +import java.math.*; + +import java.security.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class Hash { + private MessageDigest hash; + + /** + * Creates a new Hash object. + * + * @param algorithm + * + * @throws NoSuchAlgorithmException + */ + public Hash(String algorithm) throws NoSuchAlgorithmException { + hash = MessageDigest.getInstance(algorithm); + } + + /** + * + * + * @param bi + */ + public void putBigInteger(BigInteger bi) { + byte[] data = bi.toByteArray(); + putInt(data.length); + hash.update(data); + } + + /** + * + * + * @param b + */ + public void putByte(byte b) { + hash.update(b); + } + + /** + * + * + * @param data + */ + public void putBytes(byte[] data) { + hash.update(data); + } + + /** + * + * + * @param i + */ + public void putInt(int i) { + ByteArrayWriter baw = new ByteArrayWriter(); + + try { + baw.writeInt(i); + } catch (IOException ioe) { + } + + hash.update(baw.toByteArray()); + } + + /** + * + * + * @param str + */ + public void putString(String str) { + putInt(str.length()); + hash.update(str.getBytes()); + } + + /** + * + */ + public void reset() { + hash.reset(); + } + + /** + * + * + * @param data + * @param algorithm + * + * @return + * + * @throws NoSuchAlgorithmException + */ + public static byte[] simple(byte[] data, String algorithm) + throws NoSuchAlgorithmException { + MessageDigest simp = MessageDigest.getInstance(algorithm); + simp.update(data); + + return simp.digest(); + } + + /** + * + * + * @return + */ + public byte[] doFinal() { + return hash.digest(); + } +} diff --git a/src/com/sshtools/j2ssh/util/InvalidStateException.java b/src/com/sshtools/j2ssh/util/InvalidStateException.java new file mode 100644 index 0000000000000000000000000000000000000000..5ee00a8c3d3c87751724aa8fb1931be923f24b73 --- /dev/null +++ b/src/com/sshtools/j2ssh/util/InvalidStateException.java @@ -0,0 +1,44 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.14 $ + */ +public class InvalidStateException extends RuntimeException { + /** + * Creates a new InvalidStateException object. + * + * @param msg + */ + public InvalidStateException(String msg) { + super(msg); + } +} diff --git a/src/com/sshtools/j2ssh/util/OpenClosedState.java b/src/com/sshtools/j2ssh/util/OpenClosedState.java new file mode 100644 index 0000000000000000000000000000000000000000..c61e10d3db5af64f2b51f20a849545153d593e15 --- /dev/null +++ b/src/com/sshtools/j2ssh/util/OpenClosedState.java @@ -0,0 +1,61 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.13 $ + */ +public class OpenClosedState extends State { + /** */ + public static final int OPEN = 1; + + /** */ + public static final int CLOSED = 2; + + /** + * Creates a new OpenClosedState object. + * + * @param initial + */ + public OpenClosedState(int initial) { + super(initial); + } + + /** + * + * + * @param state + * + * @return + */ + public boolean isValidState(int state) { + return (state == OPEN) || (state == CLOSED); + } +} diff --git a/src/com/sshtools/j2ssh/util/SimpleASNReader.java b/src/com/sshtools/j2ssh/util/SimpleASNReader.java new file mode 100644 index 0000000000000000000000000000000000000000..4da0573a68d408ae739819cdc27481844e5f5a54 --- /dev/null +++ b/src/com/sshtools/j2ssh/util/SimpleASNReader.java @@ -0,0 +1,126 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class SimpleASNReader { + private byte[] data; + private int offset; + + /** + * Creates a new SimpleASNReader object. + * + * @param data + */ + public SimpleASNReader(byte[] data) { + this.data = data; + this.offset = 0; + } + + /** + * + * + * @param b + * + * @throws IOException + */ + public void assertByte(int b) throws IOException { + int x = getByte(); + + if (x != b) { + throw new IOException("Assertion failed, next byte value is " + + Integer.toHexString(x) + " instead of asserted " + + Integer.toHexString(b)); + } + } + + /** + * + * + * @return + */ + public int getByte() { + return data[offset++] & 0xff; + } + + /** + * + * + * @return + */ + public byte[] getData() { + int length = getLength(); + + return getData(length); + } + + /** + * + * + * @return + */ + public int getLength() { + int b = data[offset++] & 0xff; + + if ((b & 0x80) != 0) { + int length = 0; + + for (int bytes = b & 0x7f; bytes > 0; bytes--) { + length <<= 8; + length |= (data[offset++] & 0xff); + } + + return length; + } + + return b; + } + + private byte[] getData(int length) { + byte[] result = new byte[length]; + System.arraycopy(data, offset, result, 0, length); + offset += length; + + return result; + } + + /** + * + * + * @return + */ + public boolean hasMoreData() { + return offset < data.length; + } +} diff --git a/src/com/sshtools/j2ssh/util/SimpleASNWriter.java b/src/com/sshtools/j2ssh/util/SimpleASNWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..3075a60309ce5c83b94e228df66e7de581b3287e --- /dev/null +++ b/src/com/sshtools/j2ssh/util/SimpleASNWriter.java @@ -0,0 +1,105 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + +import java.io.*; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.12 $ + */ +public class SimpleASNWriter { + private ByteArrayOutputStream data; + + /** + * Creates a new SimpleASNWriter object. + */ + public SimpleASNWriter() { + this.data = new ByteArrayOutputStream(); + } + + /** + * + * + * @param b + */ + public void writeByte(int b) { + data.write(b); + } + + /** + * + * + * @param b + */ + public void writeData(byte[] b) { + writeLength(b.length); + this.data.write(b, 0, b.length); + } + + /** + * + * + * @param length + */ + public void writeLength(int length) { + if (length < 0x80) { + data.write(length); + } else { + if (length < 0x100) { + data.write(0x81); + data.write(length); + } else if (length < 0x10000) { + data.write(0x82); + data.write(length >>> 8); + data.write(length); + } else if (length < 0x1000000) { + data.write(0x83); + data.write(length >>> 16); + data.write(length >>> 8); + data.write(length); + } else { + data.write(0x84); + data.write(length >>> 24); + data.write(length >>> 16); + data.write(length >>> 8); + data.write(length); + } + } + } + + /** + * + * + * @return + */ + public byte[] toByteArray() { + return data.toByteArray(); + } +} diff --git a/src/com/sshtools/j2ssh/util/StartStopState.java b/src/com/sshtools/j2ssh/util/StartStopState.java new file mode 100644 index 0000000000000000000000000000000000000000..6ef8bc550c8b96ced77ceae27252f1303dca149e --- /dev/null +++ b/src/com/sshtools/j2ssh/util/StartStopState.java @@ -0,0 +1,64 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.15 $ + */ +public class StartStopState extends State { + /** */ + public static final int STARTED = 1; + + /** */ + public static final int STOPPED = 2; + + /** */ + public static final int FAILED = 3; + + /** + * Creates a new StartStopState object. + * + * @param initial + */ + public StartStopState(int initial) { + super(initial); + } + + /** + * + * + * @param state + * + * @return + */ + public boolean isValidState(int state) { + return (state == STARTED) || (state == STOPPED) || (state == FAILED); + } +} diff --git a/src/com/sshtools/j2ssh/util/State.java b/src/com/sshtools/j2ssh/util/State.java new file mode 100644 index 0000000000000000000000000000000000000000..b93e04e0dab6bf3907017e03526ae25642802d0d --- /dev/null +++ b/src/com/sshtools/j2ssh/util/State.java @@ -0,0 +1,169 @@ +/* + * SSHTools - Java SSH2 API + * + * Copyright (C) 2002-2003 Lee David Painter and Contributors. + * + * Contributions made by: + * + * Brett Smith + * Richard Pernavas + * Erwin Bolwidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package com.sshtools.j2ssh.util; + +import java.io.Serializable; + + +/** + * + * + * @author $author$ + * @version $Revision: 1.18 $ + */ +public abstract class State implements Serializable { + /** */ + protected int state; + + /** + * Creates a new State object. + * + * @param initialState + */ + public State(int initialState) { + this.state = initialState; + } + + /** + * + * + * @param state + * + * @return + */ + public abstract boolean isValidState(int state); + + /** + * + * + * @param state + * + * @throws InvalidStateException + */ + public synchronized void setValue(int state) throws InvalidStateException { + if (!isValidState(state)) { + throw new InvalidStateException("The state is invalid"); + } + + this.state = state; + notifyAll(); + } + + /** + * + * + * @return + */ + public synchronized int getValue() { + return state; + } + + /** + * + */ + public synchronized void breakWaiting() { + notifyAll(); + } + + /** + * + * + * @param state + * + * @return + * + * @throws InvalidStateException + * @throws InterruptedException + */ + public synchronized boolean waitForState(int state) + throws InvalidStateException, InterruptedException { + return waitForState(state, 0); + } + + /** + * + * + * @param state + * @param timeout + * + * @return + * + * @throws InvalidStateException + * @throws InterruptedException + */ + public synchronized boolean waitForState(int state, long timeout) + throws InvalidStateException, InterruptedException { + if (!isValidState(state)) { + throw new InvalidStateException("The state is invalid"); + } + + if (timeout < 0) { + timeout = 0; + } + + while (this.state != state) { + // try { + wait(timeout); + + if (timeout != 0) { + break; + } + + // } catch (InterruptedException e) { + // break; + // } + } + + return this.state == state; + } + + /** + * + * + * @return + * + * @throws InterruptedException + */ + public synchronized int waitForStateUpdate() throws InterruptedException { + // try { + wait(); + + // } catch (InterruptedException ie) { + // } + return state; + } + + /*public synchronized boolean waitForStateUpdate(long timeout) { + int oldstate = state; + if(timeout<0) + timeout=0; + try { + wait(timeout); + } catch(InterruptedException ie) { + } + return !(oldstate==state); + }*/ +}