pax_global_header00006660000000000000000000000064143404772220014517gustar00rootroot0000000000000052 comment=1595ad2cbb5b7d7e055613c1a76a3a4c82318aee commons-net-rel-commons-net-3.9.0/000077500000000000000000000000001434047722200170045ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/.asf.yaml000066400000000000000000000021141434047722200205150ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. github: description: "Apache Commons Net" homepage: https://commons.apache.org/net/ notifications: commits: commits@commons.apache.org issues: issues@commons.apache.org pullrequests: issues@commons.apache.org jira_options: link label jobs: notifications@commons.apache.org commons-net-rel-commons-net-3.9.0/.github/000077500000000000000000000000001434047722200203445ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/.github/GH-ROBOTS.txt000066400000000000000000000016331434047722200224140ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Keeps on creating FUD PRs in test code # Does not follow Apache disclosure policies User-agent: JLLeitschuh/security-research Disallow: * commons-net-rel-commons-net-3.9.0/.github/dependabot.yml000066400000000000000000000021041434047722200231710ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: 2 updates: - package-ecosystem: "maven" directory: "/" schedule: interval: "weekly" day: "friday" ignore: - dependency-name: "org.slf4j:slf4j-simple" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" day: "friday" commons-net-rel-commons-net-3.9.0/.github/workflows/000077500000000000000000000000001434047722200224015ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/.github/workflows/codeql-analysis.yml000066400000000000000000000056041434047722200262210ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '33 9 * * 4' permissions: contents: read jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'java' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository uses: actions/checkout@v3.1.0 with: persist-credentials: false - uses: actions/cache@v3.0.11 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 commons-net-rel-commons-net-3.9.0/.github/workflows/coverage.yml000066400000000000000000000031621434047722200247210ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Coverage on: [push, pull_request] permissions: contents: read jobs: build: runs-on: ubuntu-latest strategy: matrix: java: [ 8 ] steps: - uses: actions/checkout@v3.1.0 with: persist-credentials: false - uses: actions/cache@v3.0.11 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v3.6.0 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Build with Maven run: mvn -V test jacoco:report --file pom.xml --no-transfer-progress - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: files: ./target/site/jacoco/jacoco.xml commons-net-rel-commons-net-3.9.0/.github/workflows/maven.yml000066400000000000000000000037411434047722200242370ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Java CI on: [push, pull_request, workflow_dispatch] permissions: contents: read jobs: build: timeout-minutes: 7 continue-on-error: ${{ matrix.experimental }} strategy: matrix: java: [ 8, 11, 17 ] os: [ubuntu-latest] experimental: [false] # Don't need include: - java: 8 os: macos-latest experimental: false - java: 8 os: windows-latest experimental: false # include: # - java: 18-ea # experimental: true runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3.1.0 with: persist-credentials: false - uses: actions/cache@v3.0.11 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v3.6.0 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Build with Maven run: mvn -V --batch-mode -Ddoclint=all --file pom.xml --no-transfer-progress # N.B. Add -Pslf4j-simple to enable logging above commons-net-rel-commons-net-3.9.0/.github/workflows/maven_adhoc.yml000066400000000000000000000025671434047722200254020ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Java CI adhoc testing on: workflow_dispatch permissions: contents: read jobs: build: timeout-minutes: 7 runs-on: windows-latest steps: - uses: actions/checkout@v3.1.0 with: persist-credentials: false - name: Set up JDK uses: actions/setup-java@v3.6.0 with: distribution: 'temurin' java-version: 8 - name: Build with Maven run: mvn -V --batch-mode --file pom.xml --no-transfer-progress -Dtest=MainTest - name: Test exec function run: | mvn -q exec:java mvn -q exec:java -D"exec.arguments=FTPClientExample" commons-net-rel-commons-net-3.9.0/.github/workflows/scorecards-analysis.yml000066400000000000000000000047461434047722200271100ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache license, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the license for the specific language governing permissions and # limitations under the license. name: "Scorecards supply-chain security" on: branch_protection_rule: schedule: - cron: "30 1 * * 6" # Weekly on Saturdays push: branches: [ "master" ] permissions: read-all jobs: analysis: name: "Scorecards analysis" runs-on: ubuntu-latest permissions: # Needed to upload the results to the code-scanning dashboard. security-events: write actions: read id-token: write # This is required for requesting the JWT contents: read # This is required for actions/checkout steps: - name: "Checkout code" uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # 2.0.6 with: results_file: results.sarif results_format: sarif # A read-only PAT token, which is sufficient for the action to function. # The relevant discussion: https://github.com/ossf/scorecard-action/issues/188 repo_token: ${{ secrets.GITHUB_TOKEN }} # Publish the results for public repositories to enable scorecard badges. # For more details: https://github.com/ossf/scorecard-action#publishing-results publish_results: true - name: "Upload artifact" uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # 3.1.0 with: name: SARIF file path: results.sarif retention-days: 5 - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b # 2.1.22 with: sarif_file: results.sarif commons-net-rel-commons-net-3.9.0/.gitignore000066400000000000000000000001211434047722200207660ustar00rootroot00000000000000/.classpath /.project /.settings/ /target/ /.vscode/ .idea/ *.iml /site-content/ commons-net-rel-commons-net-3.9.0/BUILDING.txt000066400000000000000000000023031434047722200207400ustar00rootroot00000000000000The code requires at least Java 1.6 to build. However, Maven 3.3.1+ require Java 7 If you want to build and test the code using Java 1.6, use the profile -Pjava-1.6, e.g. $ mvn clean test -Pjava-1.6 For setting up your Maven installation to enable the use of the profile, please see: https://commons.apache.org/commons-parent-pom.html#Testing_with_different_Java_versions The latest version of Maven that runs under Java 1.6 is 3.2.5 [1] Building the site will also generally require at least Java 7 to run Maven. In particular, the Checkstyle plugin requires Java 7. To build the site from scratch, you can use: $ mvn clean site [-Pjava-1.6] Also, ensure Maven has enough memory when using Java 7: $ export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=128m" # Unix C:> set MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=128m" # Windows For Java 8+, the MaxPermSize option should be removed: $ export MAVEN_OPTS="-Xmx512m" # Unix C:> set MAVEN_OPTS="-Xmx512m" # Windows To install the component locally: $ mvn install To deploy: $ mvn deploy -Prelease -Duser.name=*ASF ID* -Pjava-1.6 [-Ptest-deploy] The test-deploy profile deploys to target/deploy rather than Nexus [1] http://maven.apache.org/docs/history.htmlcommons-net-rel-commons-net-3.9.0/CODE_OF_CONDUCT.md000066400000000000000000000016411434047722200216050ustar00rootroot00000000000000 The Apache code of conduct page is [https://www.apache.org/foundation/policies/conduct.html](https://www.apache.org/foundation/policies/conduct.html). commons-net-rel-commons-net-3.9.0/CONTRIBUTING.md000066400000000000000000000145601434047722200212430ustar00rootroot00000000000000 Contributing to Apache Commons Net ====================== You have found a bug or you have an idea for a cool new feature? Contributing code is a great way to give something back to the open source community. Before you dig right into the code there are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. Getting Started --------------- + Make sure you have a [JIRA account](https://issues.apache.org/jira/). + Make sure you have a [GitHub account](https://github.com/signup/free). + If you're planning to implement a new feature it makes sense to discuss your changes on the [dev list](https://commons.apache.org/mail-lists.html) first. This way you can make sure you're not wasting your time on something that isn't considered to be in Apache Commons Net's scope. + Submit a [Jira Ticket][jira] for your issue, assuming one does not already exist. + Clearly describe the issue including steps to reproduce when it is a bug. + Make sure you fill in the earliest version that you know has the issue. + Find the corresponding [repository on GitHub](https://github.com/apache/?query=commons-), [fork](https://help.github.com/articles/fork-a-repo/) and check out your forked repository. Making Changes -------------- + Create a _topic branch_ for your isolated work. * Usually you should base your branch on the `master` branch. * A good topic branch name can be the JIRA bug id plus a keyword, e.g. `NET-123-InputStream`. * If you have submitted multiple JIRA issues, try to maintain separate branches and pull requests. + Make commits of logical units. * Make sure your commit messages are meaningful and in the proper format. Your commit message should contain the key of the JIRA issue. * e.g. `NET-123: Close input stream earlier` + Respect the original code style: + Only use spaces for indentation. + Create minimal diffs - disable _On Save_ actions like _Reformat Source Code_ or _Organize Imports_. If you feel the source code should be reformatted create a separate PR for this change first. + Check for unnecessary whitespace with `git diff` -- check before committing. + Make sure you have added the necessary tests for your changes, typically in `src/test/java`. + Run all the tests with `mvn clean verify` to assure nothing else was accidentally broken. Making Trivial Changes ---------------------- The JIRA tickets are used to generate the changelog for the next release. For changes of a trivial nature to comments and documentation, it is not always necessary to create a new ticket in JIRA. In this case, it is appropriate to start the first line of a commit with '(doc)' instead of a ticket number. Submitting Changes ------------------ + Sign and submit the Apache [Contributor License Agreement][cla] if you haven't already. * Note that small patches & typical bug fixes do not require a CLA as clause 5 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0.html#contributions) covers them. + Push your changes to a topic branch in your fork of the repository. + Submit a _Pull Request_ to the corresponding repository in the `apache` organization. * Verify _Files Changed_ shows only your intended changes and does not include additional files like `target/*.class` + Update your JIRA ticket and include a link to the pull request in the ticket. If you prefer to not use GitHub, then you can instead use `git format-patch` (or `svn diff`) and attach the patch file to the JIRA issue. Additional Resources -------------------- + [Contributing patches](https://commons.apache.org/patches.html) + [Apache Commons Net JIRA project page][jira] + [Contributor License Agreement][cla] + [General GitHub documentation](https://help.github.com/) + [GitHub pull request documentation](https://help.github.com/articles/creating-a-pull-request/) + [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) + `#apache-commons` IRC channel on `irc.freenode.net` [cla]:https://www.apache.org/licenses/#clas [jira]:https://issues.apache.org/jira/browse/NET commons-net-rel-commons-net-3.9.0/LICENSE.txt000066400000000000000000000261361434047722200206370ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. commons-net-rel-commons-net-3.9.0/NOTICE.txt000066400000000000000000000002551434047722200205300ustar00rootroot00000000000000Apache Commons Net Copyright 2001-2022 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (https://www.apache.org/). commons-net-rel-commons-net-3.9.0/README.md000066400000000000000000000330721434047722200202700ustar00rootroot00000000000000 Apache Commons Net =================== [![GitHub Actions Status](https://github.com/apache/commons-net/workflows/Java%20CI/badge.svg)](https://github.com/apache/commons-net/actions) [![Coverage Status](https://codecov.io/gh/apache/commons-net/branch/master/graph/badge.svg)](https://app.codecov.io/gh/apache/commons-net) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/commons-net/commons-net/badge.svg?gav=true)](https://maven-badges.herokuapp.com/maven-central/commons-net/commons-net/?gav=true) [![Javadocs](https://javadoc.io/badge/commons-net/commons-net/3.9.0.svg)](https://javadoc.io/doc/commons-net/commons-net/3.9.0) [![CodeQL](https://github.com/apache/commons-net/workflows/CodeQL/badge.svg)](hhttps://github.com/apache/commons-net/actions/workflows/codeql-analysis.yml?query=workflow%3ACodeQL) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/apache/commons-text/badge)](https://api.securityscorecards.dev/projects/github.com/apache/commons-text) Apache Commons Net library contains a collection of network utilities and protocol implementations. Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois Documentation ------------- More information can be found on the [Apache Commons Net homepage](https://commons.apache.org/proper/commons-net). The [Javadoc](https://commons.apache.org/proper/commons-net/apidocs) can be browsed. Questions related to the usage of Apache Commons Net should be posted to the [user mailing list][ml]. Where can I get the latest release? ----------------------------------- You can download source and binaries from our [download page](https://commons.apache.org/proper/commons-net/download_net.cgi). Alternatively you can pull it from the central Maven repositories: ```xml commons-net commons-net 3.9.0 ``` Contributing ------------ We accept Pull Requests via GitHub. The [developer mailing list](https://commons.apache.org/mail-lists.html) is the main channel of communication for contributors. There are some guidelines which will make applying PRs easier for us: + No tabs! Please use spaces for indentation. + Respect the code style. + Create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted create a separate PR for this change. + Provide JUnit tests for your changes and make sure your changes don't break any existing tests by running ```mvn```. If you plan to contribute on a regular basis, please consider filing a [contributor license agreement](https://www.apache.org/licenses/#clas). You can learn more about contributing via GitHub in our [contribution guidelines](CONTRIBUTING.md). License ------- This code is under the [Apache Licence v2](https://www.apache.org/licenses/LICENSE-2.0). See the `NOTICE.txt` file for required notices and attributions. Donations --------- You like Apache Commons Net? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development. Additional Resources -------------------- + [Apache Commons Homepage](https://commons.apache.org/) + [Apache Issue Tracker (JIRA)](https://issues.apache.org/jira/browse/NET) + [Apache Commons Slack Channel](https://the-asf.slack.com/archives/C60NVB8AD) + [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) + `#apache-commons` IRC channel on `irc.freenode.org` Apache Commons Components ------------------------- | Component | GitHub Repository | Apache Homepage | | --------- | ----------------- | ----------------| | Apache Commons BCEL | [commons-bcel](https://github.com/apache/commons-bcel) | [commons-bcel](https://commons.apache.org/proper/commons-bcel) | | Apache Commons Beanutils | [commons-beanutils](https://github.com/apache/commons-beanutils) | [commons-beanutils](https://commons.apache.org/proper/commons-beanutils) | | Apache Commons BSF | [commons-bsf](https://github.com/apache/commons-bsf) | [commons-bsf](https://commons.apache.org/proper/commons-bsf) | | Apache Commons Build-plugin | [commons-build-plugin](https://github.com/apache/commons-build-plugin) | [commons-build-plugin](https://commons.apache.org/proper/commons-build-plugin) | | Apache Commons Chain | [commons-chain](https://github.com/apache/commons-chain) | [commons-chain](https://commons.apache.org/proper/commons-chain) | | Apache Commons CLI | [commons-cli](https://github.com/apache/commons-cli) | [commons-cli](https://commons.apache.org/proper/commons-cli) | | Apache Commons Codec | [commons-codec](https://github.com/apache/commons-codec) | [commons-codec](https://commons.apache.org/proper/commons-codec) | | Apache Commons Collections | [commons-collections](https://github.com/apache/commons-collections) | [commons-collections](https://commons.apache.org/proper/commons-collections) | | Apache Commons Compress | [commons-compress](https://github.com/apache/commons-compress) | [commons-compress](https://commons.apache.org/proper/commons-compress) | | Apache Commons Configuration | [commons-configuration](https://github.com/apache/commons-configuration) | [commons-configuration](https://commons.apache.org/proper/commons-configuration) | | Apache Commons Crypto | [commons-crypto](https://github.com/apache/commons-crypto) | [commons-crypto](https://commons.apache.org/proper/commons-crypto) | | Apache Commons CSV | [commons-csv](https://github.com/apache/commons-csv) | [commons-csv](https://commons.apache.org/proper/commons-csv) | | Apache Commons Daemon | [commons-daemon](https://github.com/apache/commons-daemon) | [commons-daemon](https://commons.apache.org/proper/commons-daemon) | | Apache Commons DBCP | [commons-dbcp](https://github.com/apache/commons-dbcp) | [commons-dbcp](https://commons.apache.org/proper/commons-dbcp) | | Apache Commons Dbutils | [commons-dbutils](https://github.com/apache/commons-dbutils) | [commons-dbutils](https://commons.apache.org/proper/commons-dbutils) | | Apache Commons Digester | [commons-digester](https://github.com/apache/commons-digester) | [commons-digester](https://commons.apache.org/proper/commons-digester) | | Apache Commons Email | [commons-email](https://github.com/apache/commons-email) | [commons-email](https://commons.apache.org/proper/commons-email) | | Apache Commons Exec | [commons-exec](https://github.com/apache/commons-exec) | [commons-exec](https://commons.apache.org/proper/commons-exec) | | Apache Commons Fileupload | [commons-fileupload](https://github.com/apache/commons-fileupload) | [commons-fileupload](https://commons.apache.org/proper/commons-fileupload) | | Apache Commons Functor | [commons-functor](https://github.com/apache/commons-functor) | [commons-functor](https://commons.apache.org/proper/commons-functor) | | Apache Commons Geometry | [commons-geometry](https://github.com/apache/commons-geometry) | [commons-geometry](https://commons.apache.org/proper/commons-geometry) | | Apache Commons Graph | [commons-graph](https://github.com/apache/commons-graph) | [commons-graph](https://commons.apache.org/proper/commons-graph) | | Apache Commons Imaging | [commons-imaging](https://github.com/apache/commons-imaging) | [commons-imaging](https://commons.apache.org/proper/commons-imaging) | | Apache Commons IO | [commons-io](https://github.com/apache/commons-io) | [commons-io](https://commons.apache.org/proper/commons-io) | | Apache Commons JCI | [commons-jci](https://github.com/apache/commons-jci) | [commons-jci](https://commons.apache.org/proper/commons-jci) | | Apache Commons JCS | [commons-jcs](https://github.com/apache/commons-jcs) | [commons-jcs](https://commons.apache.org/proper/commons-jcs) | | Apache Commons Jelly | [commons-jelly](https://github.com/apache/commons-jelly) | [commons-jelly](https://commons.apache.org/proper/commons-jelly) | | Apache Commons Jexl | [commons-jexl](https://github.com/apache/commons-jexl) | [commons-jexl](https://commons.apache.org/proper/commons-jexl) | | Apache Commons Jxpath | [commons-jxpath](https://github.com/apache/commons-jxpath) | [commons-jxpath](https://commons.apache.org/proper/commons-jxpath) | | Apache Commons Lang | [commons-lang](https://github.com/apache/commons-lang) | [commons-lang](https://commons.apache.org/proper/commons-lang) | | Apache Commons Logging | [commons-logging](https://github.com/apache/commons-logging) | [commons-logging](https://commons.apache.org/proper/commons-logging) | | Apache Commons Math | [commons-math](https://github.com/apache/commons-math) | [commons-math](https://commons.apache.org/proper/commons-math) | | Apache Commons Net | [commons-net](https://github.com/apache/commons-net) | [commons-net](https://commons.apache.org/proper/commons-net) | | Apache Commons Numbers | [commons-numbers](https://github.com/apache/commons-numbers) | [commons-numbers](https://commons.apache.org/proper/commons-numbers) | | Apache Commons Parent | [commons-parent](https://github.com/apache/commons-parent) | [commons-parent](https://commons.apache.org/proper/commons-parent) | | Apache Commons Pool | [commons-pool](https://github.com/apache/commons-pool) | [commons-pool](https://commons.apache.org/proper/commons-pool) | | Apache Commons Proxy | [commons-proxy](https://github.com/apache/commons-proxy) | [commons-proxy](https://commons.apache.org/proper/commons-proxy) | | Apache Commons RDF | [commons-rdf](https://github.com/apache/commons-rdf) | [commons-rdf](https://commons.apache.org/proper/commons-rdf) | | Apache Commons Release-plugin | [commons-release-plugin](https://github.com/apache/commons-release-plugin) | [commons-release-plugin](https://commons.apache.org/proper/commons-release-plugin) | | Apache Commons Rng | [commons-rng](https://github.com/apache/commons-rng) | [commons-rng](https://commons.apache.org/proper/commons-rng) | | Apache Commons Scxml | [commons-scxml](https://github.com/apache/commons-scxml) | [commons-scxml](https://commons.apache.org/proper/commons-scxml) | | Apache Commons Signing | [commons-signing](https://github.com/apache/commons-signing) | [commons-signing](https://commons.apache.org/proper/commons-signing) | | Apache Commons Skin | [commons-skin](https://github.com/apache/commons-skin) | [commons-skin](https://commons.apache.org/proper/commons-skin) | | Apache Commons Statistics | [commons-statistics](https://github.com/apache/commons-statistics) | [commons-statistics](https://commons.apache.org/proper/commons-statistics) | | Apache Commons Testing | [commons-testing](https://github.com/apache/commons-testing) | [commons-testing](https://commons.apache.org/proper/commons-testing) | | Apache Commons Text | [commons-text](https://github.com/apache/commons-text) | [commons-text](https://commons.apache.org/proper/commons-text) | | Apache Commons Validator | [commons-validator](https://github.com/apache/commons-validator) | [commons-validator](https://commons.apache.org/proper/commons-validator) | | Apache Commons VFS | [commons-vfs](https://github.com/apache/commons-vfs) | [commons-vfs](https://commons.apache.org/proper/commons-vfs) | | Apache Commons Weaver | [commons-weaver](https://github.com/apache/commons-weaver) | [commons-weaver](https://commons.apache.org/proper/commons-weaver) | commons-net-rel-commons-net-3.9.0/RELEASE-NOTES.txt000066400000000000000000000303051434047722200215140ustar00rootroot00000000000000 Apache Commons Net 3.9.0 RELEASE NOTES The Apache Commons Net team is pleased to announce the release of Apache Commons Net 3.9.0. Apache Commons Net library contains a collection of network utilities and protocol implementations. Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois Maintenance and bug fix release (Java 8). For complete information on Apache Commons Net, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Net website: https://commons.apache.org/proper/commons-net/ Download page: https://commons.apache.org/proper/commons-net/download_net.cgi Changes in this version include: =============================== New features: o [FTP] Add FTPClient.mdtmInstant(String). Thanks to Gary Gregory. o [FTP] Add MLSxEntryParser.parseGmtInstant(String). Thanks to Gary Gregory. o [FTP] Add FTPClient.getControlKeepAliveReplyTimeoutDuration(). Thanks to Gary Gregory. o [FTP] Add FTPClient.setControlKeepAliveReplyTimeout(Duration). Thanks to Gary Gregory. o [FTP] Add FTPClient.getControlKeepAliveTimeoutDuration(). Thanks to Gary Gregory. o [FTP] Add FTPClient.setControlKeepAliveTimeout(Duration). Thanks to Gary Gregory. o [FTP] Add FTPClient.getDataTimeout(). Thanks to Gary Gregory. o [FTP] Add FTPClient.setDataTimeout(Duration). Thanks to Gary Gregory. o [FTP] Add FTPFile.getTimestampInstant(). Thanks to Gary Gregory. o Add github/codeql-action. Thanks to Gary Gregory. Fixed Bugs: o NET-708: Use yyyy instead of YYYY in SimpleDateFormat #97. Thanks to XenoAmess. o Prevent serialization of the 4 classes that implement Serializable. It is not useful and is unlikely to work properly. o Use Math.min and Math.max method instead of manual calculations. #104. Thanks to Arturo Bernal. o NET-711: Add FTP option to toggle use of return host like CURL. Thanks to Jochen Wiedmann, Gary Gregory. o NET-642: FTPSClient execPROT removes proxy settings #90. Thanks to Yani Mihaylov, Gary Gregory. o JUnit5 assertThrows SimpleSMTPHeaderTestCase #121. Thanks to John Patrick, Gary Gregory. o JUnit5 assertThrows TestTimeInfo #120. Thanks to John Patrick, Gary Gregory. o Simplify conditions avoiding extra operations #88. Thanks to Arturo Bernal, Gary Gregory. o Remove reflection from SSLSocketUtils. Thanks to Gary Gregory. o NET-707: Process files with spaces in name for OS400 #95. Thanks to Dmytro Sylaiev, sebbASF, Gary Gregory. Changes: o Bump actions/cache from 2.1.6 to 3.0.11 #93, #102, #115, #116. Thanks to Dependabot, Gary Gregory. o Bump actions/checkout from 2.3.4 to 3.1.0 #89, #91, #100, #114. Thanks to Dependabot, Gary Gregory. o Bump actions/upload-artifact from 3.1.0 to 3.1.1 #124. Thanks to Dependabot. o Bump junit from 4.13.1 to 5.9.1 Vintage #74. Thanks to Dependabot. o Bump commons-io from 2.6 to 2.11.0 #60. Thanks to Dependabot, Gary Gregory. o Bump commons.jacoco.version from 0.8.6 to 0.8.8. Thanks to Gary Gregory. o Bump commons.japicmp.version from 0.14.3 to 0.17.1. Thanks to Gary Gregory. o Bump commons.surefire.version from 2.22.2 to 3.0.0-M7. Thanks to Gary Gregory. o Bump ftpserver-core from 1.1.1 to 1.2.0 #96. Thanks to XenoAmess, Gary Gregory. o Bump exec-maven-plugin from 3.0.0 to 3.1.0 #109. Thanks to Dependabot. o Bump commons-parent from 53 to 54 #112. Thanks to Dependabot. Historical list of changes: https://commons.apache.org/proper/commons-net/changes-report.html Enjoy! -Apache Commons Net team ----------------------------------------------------------------------------- Apache Commons Net 3.8.0 RELEASE NOTES The Apache Commons Net team is pleased to announce the release of Apache Commons Net 3.8.0. Apache Commons Net library contains a collection of network utilities and protocol implementations. Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois Maintenance and bug fix release (Java 7). For complete information on Apache Commons Net, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Net website: https://commons.apache.org/proper/commons-net/ Download page: https://commons.apache.org/proper/commons-net/download_net.cgi Changes in this version include: =============================== New features: o Add and use NetConstants. Thanks to Arturo Bernal, Gary Gregory. o Add and use SocketClient.applySocketAttributes(). Thanks to Gary Gregory. o Add FTPClient.hasFeature(FTPCmd). Thanks to Gary Gregory. o Add FTPClient.mdtmCalendar(String). Thanks to Gary Gregory. Fixed Bugs: o Fix concurrent counting of chunks in IMAPExportMbox. Thanks to Gary Gregory. o Fix possible if rare NPEs in tests. Thanks to Gary Gregory. Changes: o Bump actions/checkout from v2.3.3 to v2.3.4 #69. Thanks to Dependabot. o NET-685: Update SocketClient default connect timeout from ? to 60 seconds #51. Thanks to Simo385. o NET-695: Apply SocketClient timeout after connection but before SSL negotiation. Thanks to Gary Gregory, Possibly Cott. o Minor Improvements #71, #72. Thanks to Arturo Bernal, Gary Gregory. o Bump actions/cache from v2 to v2.1.4 #73. Thanks to Dependabot. Historical list of changes: https://commons.apache.org/proper/commons-net/changes-report.html Enjoy! -Apache Commons Net team ----------------------------------------------------------------------------- Apache Commons Net 3.7.2 RELEASE NOTES The Apache Commons Net team is pleased to announce the release of Apache Commons Net 3.7.2. Apache Commons Net library contains a collection of network utilities and protocol implementations. Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois Maintenance and bug fix release. For complete information on Apache Commons Net, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Net website: https://commons.apache.org/proper/commons-net/ Download page: https://commons.apache.org/proper/commons-net/download_net.cgi Changes in this version include: =============================== Fixed Bugs: o NET-689: Host name is not set on the SSLSocket causing isEndpointCheckingEnabled to fail. Thanks to Charlie, Gary Gregory. o Fix possible socket and input stream leak on socket exception in org.apache.commons.net.ftp.FTPClient._retrieveFile(String, String, OutputStream). Thanks to Dependabot. o NET-690: Performance issue when using the FTPClient to retrieve files #65. Thanks to payal-meh, Gary Gregory. Changes: o NET-691: Improve Javadoc for IMAPSClient #68. Thanks to Lewis John McGibbney. o Bump actions/setup-java from v1.4.2 to v1.4.3 #62. Thanks to Dependabot. o Bump junit from 4.13 to 4.13.1 #67. Thanks to Dependabot. Historical list of changes: https://commons.apache.org/proper/commons-net/changes-report.html Enjoy! -Apache Commons Net team ----------------------------------------------------------------------------- Apache Commons Net 3.7.1 RELEASE NOTES The Apache Commons Net team is pleased to announce the release of Apache Commons Net 3.7.1 Apache Commons Net library contains a collection of network utilities and protocol implementations. Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois Maintenance and bug fix release. For complete information on Apache Commons Net, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Net website: https://commons.apache.org/proper/commons-net/ Download page: https://commons.apache.org/proper/commons-net/download_net.cgi Changes in this version include: =============================== Fixed Bugs: o NET-687: [FTPS] javax.net.ssl.SSLException: Unsupported or unrecognized SSL message, #59. Thanks to Gary Gregory, Mikael, j-verse. o NET-673: Update actions/checkout from v2.3.1 to v2.3.3 #56, #61. Thanks to Dependabot. o NET-673: Update actions/setup-java from v1.4.0 to v1.4.2 #58. Thanks to Dependabot. Historical list of changes: https://commons.apache.org/proper/commons-net/changes-report.html Enjoy! -Apache Commons Net team ----------------------------------------------------------------------------- Apache Commons Net 3.7 RELEASE NOTES The Apache Commons Net team is pleased to announce the release of Apache Commons Net 3.7 Apache Commons Net library contains a collection of network utilities and protocol implementations. Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois This is mainly a bug-fix release. See further details below. This release requires a minimum of Java 7. This release is binary compatible with previous releases. However it is not source compatible with releases before 3.4, as some methods were added to the interface NtpV3Packet in 3.4 Note that the examples packages were moved under org/apache/commons/net/examples. The examples are not part of the public API, so this does not affect compatibility. Changes in this version include: New features: o NET-646: ALLO FTP Command for files >2GB o NET-615: IMAPClient could simplify using empty arguments o NET-614: IMAP fails to quote/encode mailbox names o NET-648: Add Automatic-Module-Name MANIFEST entry for Java 9 compatibility o NET-638: Telnet subnegotiations hard-limited to 512 bytes - allow override Thanks to Daniel Leong. o NET-634: Add SIZE command support Thanks to Mauro Molinari. o Add POP3ExportMbox example code o NET-674: FTPListParseEngine should support listing via MLSD Thanks to Chris Steingen. o NET-660: Next and Previous IP Address in SubnetUtils.SubnetInfo Thanks to Nagabhushan S N. Fixed Bugs: o NET-673: IMAPClient.APPEND does not always calculate the correct length o NET-643: NPE when closing telnet stream Thanks to Vasily. o NET-641: SubnetUtils.SubnetInfo.isInRange("0.0.0.0") returns true for CIDR/31, 32 Thanks to pin_ptr. o NET-639: MVSFTPEntryParser.preParse - MVS, z/OS - allow for merged Ext/Used fields Thanks to Alexander Eller. o NET-636: examples should be in org.apache.commons.net subpackage o NET-631: Bug in MVSFTPEntryParser.parseUnixList (FindBugs) o NET-584: Error when using org.apache.commons.net.ftp.FTPClient setControlKeepAliveTimeout Thanks to Kazantsev Andrey Sergeevich/Nick Manley. o NET-624: SubnetInfo#toCidrNotation: A wrong format subnet mask is allowed Thanks to Makoto Sakaguchi. o NET-623: SubnetUtils - fixed spelling errors Thanks to Makoto Sakaguchi. o NET-613: System Information Leak in ftp parser Thanks to Donald Kwakkel. o NET-663: NullPointerException when FTPClient remote verification fails Thanks to Max Shenfield. o NET-649: 227 Entering Passive Mode Thanks to Filipe Bojikian Rissi. o NET-682: MVSFTPEntryParser doesn't support Record Formats of U Thanks to richard. Changes: o NET-633: Add XOAUTH2 to IMAP and SMTP Thanks to n0rm1e. o NET-632: FTPHTTPClient - support for encoding other than UTF-8 Thanks to prakapenka. o NET-626: SubnetUtils#SubnetUtils - improved comment Thanks to Makoto Sakaguchi. o NET-625: SubnetUtils - improve construction o NET-624: SubnetInfo#getCidrSignature - improve functions Thanks to Makoto Sakaguchi. o NET-621: SubnetUtils#SubnetInfo - remove unnecessary accessors Thanks to Makoto Sakaguchi. o NET-619: SubnetUtils - improve binary netmask algorithm Thanks to Makoto Sakaguchi. o NET-678: VMS ftp LIST parsing results in empty file list Thanks to Roman Grigoriadi. Historical list of changes: https://commons.apache.org/proper/commons-net/changes-report.html For complete information on Apache Commons Net, including instructions on how to submit bug reports, patches, or suggestions for improvement, see the Apache Apache Commons Net website: https://commons.apache.org/proper/commons-net/ Download page: https://commons.apache.org/proper/commons-net/download_net.cgi commons-net-rel-commons-net-3.9.0/SECURITY.md000066400000000000000000000016041434047722200205760ustar00rootroot00000000000000 The Apache Commons security page is [https://commons.apache.org/security.html](https://commons.apache.org/security.html). commons-net-rel-commons-net-3.9.0/checkstyle-suppressions.xml000066400000000000000000000022551434047722200244430ustar00rootroot00000000000000 commons-net-rel-commons-net-3.9.0/checkstyle.xml000066400000000000000000000067041434047722200216730ustar00rootroot00000000000000 commons-net-rel-commons-net-3.9.0/findbugs-exclude-filter.xml000066400000000000000000000054661434047722200242540ustar00rootroot00000000000000 commons-net-rel-commons-net-3.9.0/pom.xml000066400000000000000000000572571434047722200203410ustar00rootroot00000000000000 4.0.0 org.apache.commons commons-parent 54 commons-net commons-net 3.9.0 Apache Commons Net Apache Commons Net library contains a collection of network utilities and protocol implementations. Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois https://commons.apache.org/proper/commons-net/ 2001 1.8 1.8 ${commons.javadoc7.java.link} net org.apache.commons.net NET 12310487 site-content https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-net false false 3.9.0 RC1 (Requires Java ${maven.compiler.target} or later) 3.8.0 true Gary Gregory 86fdc7e2a11262cb true 5.9.1 0.8.8 0.17.1 3.0.0-M7 false false scm:git:https://gitbox.apache.org/repos/asf/commons-net scm:git:https://gitbox.apache.org/repos/asf/commons-net https://gitbox.apache.org/repos/asf/commons-net jira https://issues.apache.org/jira/browse/NET apache.website Apache Commons Site scm:svn:https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-net/ org.junit.jupiter junit-jupiter-api test org.junit.vintage junit-vintage-engine test org.junit.jupiter junit-jupiter-engine test org.apache.ftpserver ftpserver-core 1.2.0 test commons-io commons-io 2.11.0 test org.apache.commons commons-lang3 3.12.0 test clean apache-rat:check javadoc:javadoc checkstyle:check package japicmp:cmp com.github.spotbugs spotbugs-maven-plugin findbugs-exclude-filter.xml org.apache.maven.plugins maven-checkstyle-plugin ${commons.checkstyle-plugin.version} org.apache.maven.plugins maven-jar-plugin **/examples/** ${commons.module.name} org.apache.maven.plugins maven-source-plugin **/examples/** org.apache.maven.plugins maven-surefire-plugin **/*FunctionalTest.java **/POP3*Test.java ${commons.net.trace_calls} ${commons.net.add_listener} maven-assembly-plugin src/assembly/bin.xml src/assembly/src.xml gnu maven-antrun-plugin package run org.codehaus.mojo build-helper-maven-plugin attach-artifacts package attach-artifact target/commons-net-ftp-${project.version}.jar jar ftp target/commons-net-examples-${project.version}.jar jar examples org.apache.maven.plugins maven-javadoc-plugin *.examples.* maven-resources-plugin copy-resources pre-site copy-resources ${basedir}/target/site/examples src/main/java/org/apache/commons/net/examples **/Main.java false org.apache.maven.plugins maven-scm-publish-plugin javadocs org.codehaus.mojo exec-maven-plugin 3.1.0 java org.apache.commons.net.examples.Main org.apache.maven.plugins maven-checkstyle-plugin ${commons.checkstyle-plugin.version} ${basedir}/checkstyle.xml config_loc=${basedir} ${basedir}/checkstyle-suppressions.xml false com.github.spotbugs spotbugs-maven-plugin findbugs-exclude-filter.xml org.apache.maven.plugins maven-javadoc-plugin *.examples.* org.apache.maven.plugins maven-checkstyle-plugin ${commons.checkstyle-plugin.version} ${basedir}/checkstyle.xml config_loc=${basedir} ${basedir}/checkstyle-suppressions.xml false slf4j-simple true true org.slf4j slf4j-simple 1.7.21 test Jeffrey D. Brekke brekke Jeff.Brekke@qg.com Quad/Graphics, Inc. Steve Cohen scohen scohen@apache.org javactivity.org Bruno D'Avanzo brudav bruno.davanzo@hp.com Hewlett-Packard Daniel F. Savarese dfs dfs@apache.org <a href="http://www.savarese.com/">Savarese Software Research</a> Rory Winston rwinston rwinston@apache.org Rory Winston rwinston@checkfree.com ggregory Gary Gregory ggregory at apache.org https://www.garygregory.com The Apache Software Foundation https://www.apache.org/ PMC Member America/New_York https://people.apache.org/~ggregory/img/garydgregory80.png Henrik Sorensen henrik.sorensen@balcab.ch Jeff Nadler jnadler@srcginc.com William Noto wnoto@openfinance.com Stephane ESTE-GRACIAS sestegra@free.fr Dan Armbrust daniel.armbrust.list@gmail.com Yuval Kashtan Joseph Hindsley Rob Hasselbaum rhasselbaum@alumni.ithaca.edu Mario Ivankovits mario@ops.co.at Naz Irizarry MITRE Corp Tapan Karecha tapan@india.hp.com Jason Mathews MITRE Corp Winston Ojeda Winston.Ojeda@qg.com Quad/Graphics, Inc. Ted Wise ctwise@bellsouth.net Bogdan Drozdowski bogdandr # op dot pl commons-net-rel-commons-net-3.9.0/src/000077500000000000000000000000001434047722200175735ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/assembly/000077500000000000000000000000001434047722200214125ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/assembly/bin.xml000066400000000000000000000055661434047722200227200ustar00rootroot00000000000000 bin tar.gz zip LICENSE* NOTICE* README* RELEASE-NOTES.txt target commons-net-${project.version}.jar target/site/apidocs apidocs **/* target commons-net-${project.version}-sources.jar target commons-net-examples-${project.version}.jar src/main/java/org/apache/commons/net/examples org/apache/commons/net/examples **/* commons-net-rel-commons-net-3.9.0/src/assembly/src.xml000066400000000000000000000034431434047722200227270ustar00rootroot00000000000000 src tar.gz zip ${artifactId}-${version}-src .travis.yml BUILDING.txt checkstyle*.xml CONTRIBUTING.md findbugs-exclude-filter.xml LICENSE* NOTICE* pom.xml README* RELEASE-NOTES.txt src commons-net-rel-commons-net-3.9.0/src/changes/000077500000000000000000000000001434047722200212035ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/changes/changes.xml000066400000000000000000002420311434047722200233370ustar00rootroot00000000000000 Apache Commons Net Release Notes Apache Commons developers Use yyyy instead of YYYY in SimpleDateFormat #97. Prevent serialization of the 4 classes that implement Serializable. It is not useful and is unlikely to work properly. Use Math.min and Math.max method instead of manual calculations. #104. Add FTP option to toggle use of return host like CURL. FTPSClient execPROT removes proxy settings #90. JUnit5 assertThrows SimpleSMTPHeaderTestCase #121. JUnit5 assertThrows TestTimeInfo #120. Simplify conditions avoiding extra operations #88. Remove reflection from SSLSocketUtils. Process files with spaces in name for OS400 #95. [FTP] Add FTPClient.mdtmInstant(String). [FTP] Add MLSxEntryParser.parseGmtInstant(String). [FTP] Add FTPClient.getControlKeepAliveReplyTimeoutDuration(). [FTP] Add FTPClient.setControlKeepAliveReplyTimeout(Duration). [FTP] Add FTPClient.getControlKeepAliveTimeoutDuration(). [FTP] Add FTPClient.setControlKeepAliveTimeout(Duration). [FTP] Add FTPClient.getDataTimeout(). [FTP] Add FTPClient.setDataTimeout(Duration). [FTP] Add FTPFile.getTimestampInstant(). Add github/codeql-action. Bump actions/cache from 2.1.6 to 3.0.11 #93, #102, #115, #116. Bump actions/checkout from 2.3.4 to 3.1.0 #89, #91, #100, #114. Bump actions/upload-artifact from 3.1.0 to 3.1.1 #124. Bump junit from 4.13.1 to 5.9.1 Vintage #74. Bump commons-io from 2.6 to 2.11.0 #60. Bump commons.jacoco.version from 0.8.6 to 0.8.8. Bump commons.japicmp.version from 0.14.3 to 0.17.1. Bump commons.surefire.version from 2.22.2 to 3.0.0-M7. Bump ftpserver-core from 1.1.1 to 1.2.0 #96. Bump exec-maven-plugin from 3.0.0 to 3.1.0 #109. Bump commons-parent from 53 to 54 #112. Add and use NetConstants. Add and use SocketClient.applySocketAttributes(). [FTP] Add FTPClient.hasFeature(FTPCmd). [FTP] Add FTPClient.mdtmCalendar(String). [IMAP] Fix concurrent counting of chunks in IMAPExportMbox. Fix possible if rare NPEs in tests. Bump actions/checkout from v2.3.3 to v2.3.4 #69. Update SocketClient default connect timeout from ∞ to 60 seconds #51. Apply SocketClient timeout after connection but before SSL negotiation. Minor Improvements #71, #72. Bump actions/cache from v2 to v2.1.4 #73. Host name is not set on the SSLSocket causing isEndpointCheckingEnabled to fail. Fix possible socket and input stream leak on socket exception in org.apache.commons.net.ftp.FTPClient._retrieveFile(String, String, OutputStream). Performance issue when using the FTPClient to retrieve files #65. Improve Javadoc for IMAPSClient #68. Bump actions/setup-java from v1.4.2 to v1.4.3 #62. Bump junit from 4.13 to 4.13.1 #67. [FTPS] javax.net.ssl.SSLException: Unsupported or unrecognized SSL message, #59. Update actions/checkout from v2.3.1 to v2.3.3 #56, #61. IMAPClient.APPEND does not always calculate the correct length ALLO FTP Command for files >2GB IMAPClient could simplify using empty arguments IMAP fails to quote/encode mailbox names NPE when closing telnet stream Add Automatic-Module-Name MANIFEST entry for Java 9 compatibility SubnetUtils.SubnetInfo.isInRange("0.0.0.0") returns true for CIDR/31, 32 Telnet subnegotiations hard-limited to 512 bytes - allow override MVSFTPEntryParser.preParse - MVS, z/OS - allow for merged Ext/Used fields examples should be in org.apache.commons.net subpackage Add SIZE command support Add POP3ExportMbox example code Add XOAUTH2 to IMAP and SMTP FTPHTTPClient - support for encoding other than UTF-8 Bug in MVSFTPEntryParser.parseUnixList (FindBugs) Error when using org.apache.commons.net.ftp.FTPClient setControlKeepAliveTimeout SubnetUtils#SubnetUtils - improved comment SubnetUtils - improve construction SubnetInfo#getCidrSignature - improve functions SubnetInfo#toCidrNotation: A wrong format subnet mask is allowed SubnetUtils - fixed spelling errors SubnetUtils#SubnetInfo - remove unnecessary accessors SubnetUtils - improve binary netmask algorithm System Information Leak in ftp parser VMS ftp LIST parsing results in empty file list FTPListParseEngine should support listing via MLSD NullPointerException when FTPClient remote verification fails 227 Entering Passive Mode Next and Previous IP Address in SubnetUtils.SubnetInfo MVSFTPEntryParser doesn't support Record Formats of U TFTPClient assumes that lastBlock == 0 only once Allow TFTPServer.java to bind to a specific network adapter Apache Commons TFTP does not reject request replies that originate from a control port. TFTP sendFile retry broken Allow TFTP socket IO tracing NullPointerException when disconnecting TelnetClient twice with JDK 7 Failure to parse times from SYST_L8 systems that report as "WINDOWS Type: L8" TFTP send and receive don't have progress indication FTPClient.setPassiveNatWorkaround assumes host is outside site local range FTPClient.mlistFile incorrectly handles MLST reply FTP does not validate command reply syntax fully DefaultUnixFTPFileEntryParserFactory Issue (leading spaces removal configuration) POP3Mail example: support host port; allow reading password from Console/stdin/environment FTP fails to parse listings for Solaris 10 FTPd in Japanese Add shorthand FTPClientConfig constructor HostnameVerifier is called with ip addess instead of the provided hostname TelnetClient._closeOutputStream unhandled exception from FilterOutputStream.close plainSocket in FTPSClient is never closed FTPClient.getReplyString() returns wrong value after connect() Alternative password input methods for IMAP examples More tests for Feb 29 handling. Don't use Feb 29 for short future date tests Documentation tweaks SimpleSMTPHeader fails to supply the required Date: header SimpleSMTPHeader does not allow for missing To: field SMTPClient.sendSimpleMessage() silently ignores failed recipients Update Javadoc SSL/TLS SocketClients do not verify the hostname against the certificate Allow FTPClient to use SYST response if system type is not specified in configuration FTPClientExample should support setting the date format FTPHTTPClient should use socket factory to create sockets UnixFTPEntryParser Drops Leading Spaces from File Names examples/Main now uses a property file to define aliases instead of scanning class files SocketTimeoutException connecting a FTP server via an HTTP Proxy FTPListParseEngine does not provide access to raw responses Add FTPClient method to return an FTPFile from an MDTM command FTPFile.toFormattedString - allow specification of TimeZone for display FTPFile.toFormattedString should print only signficant parts of the parsed date MLSxEntryParser needs test cases; parsing is too lax FTPFile.toFormattedString prints user and group in wrong order FTPClient.initiateListParsing does not correctly check if parserKey was cached Simplify TelnetOptionHandler class hierarchy FTPClient.getModificationTime(filename) returns complete received line including response code and EOL Strip the response code and EOL Make SubnetInfo.isInRange(int) public Default FTPClient bufferSize results in very slow retrieve transfers Fix code in Util#copyStream (also copyReader) that failed to use the proper default for buffer size 0 Util copyReader calls CopyStreamListener.bytesTransferred with the incorrect value for bytesTransferred Added control character processing to TelnetClientExample There is a lack of documentation regarding setControlKeepAliveTimeout Telnet does not convert LF to CRLF in ASCII mode telnet: spy read EOL is reversed Article#printThread should have option to use any PrintStream NPE if Threader.thread invoked with empty list or with null array IMAP FETCH example IMAPExportMbox can export selected nessages from an IMAP folder IMAP FETCH can overflow reply buffer; provide for partial responses Unnecesssary call to getReplyString() if no listeners configured input parameter of org.apache.commons.net.ftp.FTP.__getReply(boolean) is not used SubnetUtils throws exception on valid input Add SimpleNTPServer as example and for testing parser problem occurs if the filename contains one or more characters of which the second byte of Shift-JIS code is 0x85 Fix NT parser Added control encoding option to FTPClientExample Added missing set methods on NTP class and interface Avoid greedy matches within a regex SubnetUtils("0.0.0.0/0") does not behave as expected Fixed range checking so network and broadcast addresses are treated as unsigned ints SubnetUtils.SubnetInfo.getAddressCount() can overflow as it returns an int FTPClient sample in class javadoc "bug" Apache Commons Net 3.3 has a performance issue FTPClient#reinitialize is package protected Downloading files or members from the AS400 QSYS file system is not supported FTPClient#initFeatureMap should not initialize empty map if reply code is 530 IMAP APPEND multiple issues in IMapClient. Deprecated unusable append methods. Added new append method, as well as example IMapImportMbox class to make use of it. Exception for new SubnetUtils("0.0.0.0/0"). AuthenticatingSMTPClient needs a constructor with the isImplicit argument for SSL Race Condition on TelnetClient.disconnect() and TelnetInputStream.run() java.lang.IllegalStateException: Queue is full! Cannot process another character. User specified bufferSize reset to default when FTPClient is disconnected or reinitialized resulting in performance degradation. Option to disable private IP replacement in FTP passive mode. AuthenticatingSMTPClient does not support non-default encoding Always call FTPClient#setFileType after connection. Not all servers default to ASCII. FTPClient setSendBufferSize and setReceiveBufferSize on data socket. The previous fix caused performance problems. Added new getters and setters for the SO_SNDBUF and SO_RCVBUF values to be used on the data socket. Util copyReader/copyStream classes should use default buffer size for non-positive buffer size parameters. FTPCommand conversion to use enum; added FTPCmd emum and deprecated FTPCommand. Wrong passivHost when using FTPHTTPClient with EPSV FTPClient.CSL.cleanUp() fails to restore timeout value on exception FTPClient.printWorkingDirectory() incorrectly parses certain valid PWD command results retrieveFileStream fails randomly or hangs Remove unnecessary Base64 methods. Support XOAUTH. Base64.CHUNK_SEPARATOR should be private Base64.encodeBase64(byte[], boolean, boolean, int) does not calculate output size correctly for unchunked output. Regression: TelnetInputStream#available() blocks. FTPS: Hook to customize _openDataConnection_ SSLSocket before startHandshake() is called. Implement _openDataConnection(String, String) method to properly interface with FTPClient.openDataConnection(String, String) TelnetClient hangs when reader-thread startup is delayed. listFiles bug with folder that begins with "-". Clarify Javadoc. FTPClient setSoTimeout (int time) will result in NullPointerException. Clarify Javadoc. Request for native support for socks proxy routing with Commons net FTP. FtpClient sends REST when calling listFiles. Clarified Javadoc. FTPClient setSendBufferSize and setReceiveBufferSize on data socket. FTPClient in PASSIVE_LOCAL_DATA_CONNECTION_MODE cannot work when host have several different IP. IMAPClient#fetch() does not handle literal strings. MVSFTPEntryParser.parseSimpleEntry - ArrayIndexOutOfBoundsException. Bug in documentation for FTPClient. The examples can now be run using "java -jar commons-net-examples-m.n.jar". This will automatically include the main net jar in the classpath. See documentation. FTPClientExample now supports "-A" for anonymous login StringIndexOutOfBoundsException: String index out of range: -1 if server respond with root is current directory. FTPTimestampParserImpl fails to parse future dates correctly on Feb 28th in a leap year. [FTP] mlistDir doc should be "MLSD" not "MSLD". [FTP] Allow user to provide default value in case SYST command fails. POP3Client.capa() should call POP3Client.getAdditionalReply() TFTP implementation subject to Sorcerer's Apprentice Syndrome TFTP does not handle RFC 783 retransmits TelnetInputStream doesn't support non-blocking IO when reader thread is not enabled FTP should support reporting NATed external IP address Commons NET site should link to the examples FTP using HTTP proxy not working FTPClient.storeFIle might fail when ControlKeepAliveTimeout is set (ditto for FTPCLient.retrieveFile) FTPS: Hook to customize _openDataConnection_ SSLSocket before startHandshake() is called Can't login to POP3S Server using explicit mode FTPClient fails to close local listener socket when command socket channel encounter "ReadTimeoutException" [FTP] Support for SYST "Mac OS" listing - "MACOS Peter's Server" [FTP] _openDataConnection_, __storeFile, and __storeFileStream should be protected and take String for FTP command. Likewise for receiveFile and receiveFileStream. [Telnet] Increasing sub-negotiation message holder array size SubnetUtils throws ArrayIndexOutOfBoundsException for new SubnetUtils( "1.2.3.4/32" ).getInfo().getAllAddresses() Problem connecting to TLS/SSL SMTP server using explicit mode. [Site] typo in migration how-to. FTPClient truncates file (storeFile method). Fix bug introduced in release 3.0. Change lenientFutureDates to default to true. This means short dates will be parsed as the current year when the host clock is up to 1 day ahead of the client clock. FTPSSocketFactory does not override createSocket(); causes java.net.SocketException: Unconnected sockets not implemented. ftp data connection does not use connectTimeout. Option to override SSL negotiation. Make FTPSClient#execAuth() and FTPSClient#sslNegotiation() protected IMAP, NNTP, POP3 and SMTP classes uses BufferedReader for control channel, which does not follow the standard. Changed reader to CRLFLineReader. FTP class uses BufferedReader for control channel, which does not follow the standard. Changed reader to CRLFLineReader. AS400 file timestamp format is wrong. Workround exists. Remove semi-redundant check in SubnetUtils.calculate(). Should Telnet class Exception blocks write to System.err? Catch blocks removed, and throws clauses added to allow caller to more easily detect and recover. FTPSClient does not handle AUTH or ADAT and only partially handles PBSZ. FTPSCommand should be deprecated. Better handling of CIDR/31 and CIDR/32 where isInclusive = false. Return 0 for address count, and 0.0.0.0 for each of the addresses Move ProtocolCommandSupport to SocketClient. Should the sendCommandWithID() methods be public? Made methods private, and deleted currently unused ones. Are the sendUntaggedCommand() methods needed? Renamed the method as sendData(), as it's not a command. Use enum for IMAPCommand. Added basic IMAP/IMAPS implementation. Unix parser should ignore "total nnn" lines. Article.addHeaderField() is currently write-only - there is no way to retrieve the headers - is it needed? Method was removed, along with the field. ntp.TimeStamp uses incorrect lazy initialisation of static fields simpleFormatter and utcFormatter. Parsing is inefficient, as it parses everything twice. VMSVersioningFTPEntryParser#preParse should not call super.preParse(). TelnetInputStream has various threading bugs. TelnetClient use of FromNetASCIIInputStream and ToNetASCIIOutputStream breaks binary mode. See also NET-387. FTP does not apply timeout to initial responses. KeyManagerUtils - the KeyManager is not efficient. KeyManagerUtils - allow alias to be omitted when there is only one private key in the store A KeyManager is required when the protection level is set to 'P' with FTPSClient on active mode. Added KeyManagerUtils class to simplify provision of client certificates. FEAT response parsing. Added FTPClient methods: boolean hasFeature(feature [,option]), String fetaureValue(feature), String[] featureValues(feature) FTPClient - support for processing arbitrary commands that only use the control channel FTP listing should support MLST and MLSD. NLST does not take notice of HiddenFiles setting. NNTP Listgroups not working - broken server implementation. DotTerminatedMessageReader should extend BufferedReader, rather than Reader. ParserInitializationException doesn't use standard JDK exception chaining FTPSClient: java.security.cert.CertificateException: No X509TrustManager implementation available if trustManager == null Create TrustManagerFactory to provide custom TrustManagers. FTPSClient not properly supporting CCC and PROT P. Threader.thread should accept an Iterable rather than a List. "Unconnected sockets not implemented" when using FTPSClient Added disconnect() override which resets the socket factories to their defaults "java.net.SocketException: Broken pipe" when calling "TelnetClient.sendAYT()" Added SocketClient#isAvailable() method to perform additional checks on a socket. Add streaming methods (corresponding to array methods) to NNTPClient. FTPClient.listFiles() does not work properly, if remote server speaks German. Match non-space{3} instead of A-Za-z{3} FTPClientConfig: setServerLanguageCode and setShortMonthNames do not work. Ensure that config is passed to all parsers that can use it. NNTPClient has problems with group listings for large groups. Possible NPE in Threader.java nntp.Article is very inefficient and incorrect. The FTP client should autodetect the control encoding. Can't connect to a server behind firewall in passive mode. Queue is full TelnetInputStream. Implement Telnet Command sender. Telnet client: not properly handling IAC bytes within subnegotiation messages: - failing to double IACs on output - failing to de-double IACs in input Telnet client: Support Client-initiated Subnegotiation Messages. Telnet client: Support Listener Notification of Incoming Data. Incorrect error handling in method initiateListParsing of FTPClient. SASL PLAIN and CRAM-MD5 authentication. The POP3 client does not support SSL/TLS connections. Removed deprecated unused fields from FTPSClient: - KEYSTORE_ALGORITHM, PROVIDER, STORE_TYPE, TRUSTSTORE_ALGORITHM Implement A Keepalive Mechanism. Control channel keepalive implemented for the following methods: appendFile, storeFile, storeUniqueFile, retrieveFile. StackOverflowError in Threader. POP3MessageInfo fields should be final. Get rid of using deprecated API in VMSFTPEntryParser. The method VMSFTPEntryParser.parseFileList(InputStream listStream) should not be present. FTPFileEntryParser API samples are wrong. Use properties file (/systemType.properties) to handle new OS-type auto-detection. Commons net ftp cannot handle unknown type parser and should allow override of parser through vm argument. The system property "org.apache.commons.net.ftp.systemType" can be used to provide the system type. Unhandled SecurityException in DefaultFTPFileEntryParserFactory.createFileEntryParser when using applets. DefaultFTPFileEntryParserFactory.createFileEntryParser(String key) always tries to load a class. New FTPClient method to retrieve all directory names in the current working directory. Added methods listDirectories(), listDirectories(String path). The SMTPClient does not support authentication. The SMTP client does not support SSL/TLS connections. Implement copy Listener in FTPClient file operations. CopyStreamAdapter unconditionally resets the CopyStreamEvent source and is inefficient. examples.nntp.NNTPUtils does not compile APOP authentication fails most of the time. Fix by adding leading 0 if necessary. FromNetASCIIInputStream can throw a NullPointerException FTPClient.remoteAppend(String filename) uses STOR instead of APPE Incorrect parsing of timestamp on Windows CE Fix parsing to allow for new-style DOS listing using 24hr clock rather than AM/PM ftp.FTPClient.initiateListParsing(String parserKey, String pathname) can call createFileEntryParser with null systemName. Fix this by adding getSystemType() which does not return null, and deprecating getSystemName(). Add support for FTPFileFilter filters. New classes FTPFileFilter, FTPFileFilters, new methods: FTPListParseEngine#getFiles(FTPFileFilter filter) FTPClient.listFiles(String pathname, FTPFileFilter filter) Optionally enable EPSV with IPv4; Only send EPRT with IPv6. Fix incorrect port used with EPRT. Allow activeMaxPort == activeMinPort in getActivePort() method. FromNetASCIIInputStream.read(byte[], int, int) may change length passed to superclass if not doing conversion Testcase to show WindowSizeOptionHandler is working OK The method VMSFTPEntryParser.parseFileList(InputStream listStream) should not be present. Partial fix - marked method as deprecated and to be removed Telnet EOR is "consumed" by TelnetInputStream when in BINARY transmission. Send notification to TelnetNotificationHandler. TelnetInoutStream#__read() bug in the __receiveState handling for the _STATE_IAC state. SocketClient should ensure input and output streams are closed FTP: initiateListParsing should not cache entryParser Improvement to isInRange method in SubnetUtil.SubnetInfo class FTPClient.listFiles() corrupts file name in certain circumstances Telnet spyStream NullPointerException Deprecated the following unused fields from org.apache.commons.net.ftp.FTPSClient: KEYSTORE_ALGORITHM, PROVIDER, STORE_TYPE, TRUSTSTORE_ALGORITHM Fix site reports Add support for setting external host ip/port range Add fix and testcase for DotTerminatedMessageReader Add support for IPv6 EPRT/EPSV Fix SubnetUtils for /32 subnets and add inclusive host count flag Fix NPE when listHiddenFiles was on UNIXFTPEntryParser didn't preserve trailing whitespace in files method SubnetUtils.SubnetInfo.isInRange(addr) returns incorrect result Method createServerSocket of FTPSSocketFactory never called and thus UseClientMode is incorrect in a secured ftp transfer using active mode. Fix inconsistent command list in FTPCommand DefaultFTPFileEntryParserFactory did not work with Netware FTP server returning "NETWARE TYPE: L8" FTP.getReplyStrings() returned array of null Strings UnixFTPEntryParser regex did not match some directory entries SubnetUtils.SubnetInfo.isInRange(...) returned incorrect values SubnetUtils.SubnetInfo.isInRange(...) behavior not documented SubnetUtils did not handle /31 and /32 CIDRs well UnixFTPEntryParser failed to parse entry in certain conditions FTPClient.listFiles() corrupted file name in certain circumstances Moved class "ThreadContainer" from Threader.java into its own source file FTPSClient should accept a pre-configured SSLContext SubnetUtils / SubNetInfo toString() implementations Improve NNTPClient handling of invalid articles Refactor examples package. Javadoc fixes, improvements, and refactoring. Apply MFMT patch Fix copying of reply lines collection Fix incorrect NNTP constant Restore socket state after CCC command Example code in FTPClient doesn't compile Fix inconsistent handling of socket read/write buffer size UnixFTPEntryParser fails to parse some entries One of the "connect" method in class org.apache.commons.net.SocketClient doesn't handle connection timeout properly Add null check in TelnetClient::disconnect(). Remove deprecated FTPFileIterator and FTPFileList classes. Add connection timeout functionality to SocketClient. Make the KeyManager and TrustManager settable (niklas@protocol7.com). Patch FTPSClient to set default SSLServerSocketFactory. Thanks niklas@protocol7.com Patch to prevent TFTPClient dropping last packet. Thanks palm@poplarware.com Change isConnected() method to delegate to underlying socket connection. FTPS (TLS and SSL) is now supported. Thanks to Jose Juan Montiel, Paul Ferraro, and Satoshi Ishigami. Commons::Net now uses Maven 2. The project.xml has been replaced with a pom.xml, and the source tree layout has been changed accordingly. Removed old ftp2 proposal directories. Commons::Net now uses JDK regex functionality, saving on an extra [oro] dependency. There are now no external dependencies required. Various syntactic issues (FindBugs issues, JDK 5.0 generics support) Applied Rob Hasselbaum's rhasselbaum -> alumni.ithaca.edu patch for PR 38688 fixing a TelnetInputStream hang. Exposed control connection of FTP class via _controlInput_ and _controlOutput_ protected member variables in response to PR 38309 reported by josejuan.montiel@gmail.com. Reverted PR 32859 patch to TFTPClient because it caused final packets to not be sent. Make FTPClient extend SocketClient instead of TelnetClient. From jhindsley@providerlink.com Adds an "e" symbolic link flag to the Unix FTP parser. From denisgaebler@netscape.net Allow hidden files to be listed. Thanks to mario@ops.co.at Remove reflective check for Socket::isConnected() (no longer needed) Added WindowSizeOptionHandler for TelnetClient. Thanks to yuvalkashtan@gmail.com Refactored *Client classes under net/ package into separate subpackages, and move PrintCommandListener out of the examples/ package. Added an ant target to the Maven build to generate an FTP-only jar file, for clients who wish to use only FTP-based functionality. Custom SocketFactory interface has been replaced with the JDK SocketFactory implementation. Added ServerSocketFactory instance to SocketClient. Removed redundant FTP.IMAGE_FILE_TYPE flag. Added heavily updated MVSFTPEntryParser from henrik.sorensen@balcab.ch Removed deprecated classes FTPFileListParser, FTPFileListParserImpl, and DefaultFTPFileListParser. Also removed associated deprecated methods from FTPClient. Added encoding to FingerClient. From Ulrich Mayring. Catch BindException in RCommandClient::connect(). Add encoding specifier to SMTPClient. Add setters for socket send/receive buffer size to SocketClient. Fix PASV specifiers that broke previously. From Chris Eagle. Catch NPE in FTP parser factory method. Don't bind a UDP socket to NTP protocol port. Better handling of user and group names with embedded spaces in FTP listings. Add configurable multiline parsing. Add fix for broken leap year date parsing. Add SubnetUtils class (suggested by Kenny McLeod) Add Unix-type handling for UNKNOWN Type: L8 syst() message systems. Allow FTPTimestampParserImpl to take a predefined Calendar instance representing current time. Replace Exception with IOException VMS file permission parsing TelnetInputStream swallows interruptedexception as IOException the data connection socket is not closed when an IOException occurred ParserInitializationException when connecting to a Unix FTP server: comparison string must be upper case FTPFileEntryParserImpl.preParse() doesn't remove unparsable entries at the end of the file list Applied patches for defect 37113. Code incompatible with jdk 1.3. Original patch submitted by Andrea Rombald Applied patches for defect 37522. updated project.xml to correct compatibility level. Fixed typo in method name. FTP.removeCommandListener() was missing the L. Problem reported by Per.Lindberger@linkon.se. Applied fix for PR 33942 and PR 31793. Original patch submitted by mario@ops.co.at TFTPClient was ignoring final ACK (PR 32859). Thanks to perttu.auramo@ekahau.com Applied fix for ACL parsing in the FTP client (PR 33972). Submitted by robertalasch@yahoo.com Added missing NTP/SNTP unit tests to the codebase. Applied fix for POP3Client returning empty reply strings (PR 34133). Thanks to sammy_c@lineone.net NTP port parameter was being ignored (PR 34219). Fixed by felix.eichhorn@3soft.de An FTP parser for MVS was added. Submitted by wnoto@openfinance.com Added functionality for extensible parsing of FTP responses, using a configurable format string. This should enable the FTP client to operate across many different locales and date formats. Applied patch for PR 31793. Thanks to mario@ops.co.at Added message threading functionality to the NNTP client. Added return code 521 to FTPReply.java - this should obviate the need for the Ant FTP task to manually declare it. Add explicit notify() in TelnetInputStream::read(), so available() returns an accurate value. Thanks to tpalkot@gmail.com. Added SNTP/NTP components into the Commons-Net codebase, courtesy of Jason Matthews. Added POP3 test suite, courtesy of Mike George mike.george@comcast.net. Applied fix for FTPClient returning null for certain timestamp formats (BUG #30737) Build.xml fixes - dont include example classes in redistributable .jar, remove test dependency from javadoc target, and exclude private members from generated javadoc. Fixed bug in TFTPClient::setMaxTimeout(), spotted by steve@widge.net Some changes to facilitate compilation under JDK 5.0 Return correct NNTP article count when high and low watermarks are 0. Spotted by jmordax@terra.es Remove trailing null byte in TFTP packets. Thanks to gerard.dens@alcatel.be Many javadoc fixes. Allow FTPClient to set transfer buffer size. Ensure consistent handling of encoding throughout FTPClient operations. Patch submitted by leif@tanukisoftware.com. Fix TelnetClient zombie thread issue Fixed regression from migration to new parsers. Most of the new parsers parsed the file size as an integer instead of a long. Changed all of them to set the size to long. This problem was detected by the reporter of: https://issues.apache.org/bugzilla/show_bug.cgi?id=30345 fixed bug in the way FTPClient.listFiles worked when a directory was not specified. Current directory was not being 'remembered'. This was most problematic in the dependent ftp task of Ant. fixed handling of certain unusual "special" file types in the Unix parser. changed code that rendered package uncompilable under JDK 1.2 Mario Ivankovits mario@ops.co.at added functionality supporting correct handling of the "dirstyle" attribute of NT and OS400 servers that allows them to mimic Unix ftp servers. and a bug fix affecting handling of sticky and suid bits on Unix FTP servers. Mario Ivankovits mario@ops.co.at added parser for OS400. Added a functional junit test testing list parsing against real servers and fix several bugs found through this test. Ted Wise ctwise@bellsouth.net provided a patch to add the XHDR extended NNTP command. Deprecated FTPFileListParser interface, DefaultFTPFileListParser class, and the FTPClient.listFiles methods that accepted an FTPFileListParser parameter. These deprecated classes and methods will be removed in version 2.0. Added org.apache.commons.net.parser.FTPFileEntryParserFactory interface and a default implementation: DefaultFTPFileEntryParserFactory. This addition facilitates the autodetection of which FTPFileEntryParser to use to generate listings. FTPClient.listFiles methods were added that implement autodetection. Rory Winston Rory.Winston@telewest.co.uk provided patches to add the following extended NNTP commands to NNTPClient: XOVER, AUTHINFO USER, AUTHINFO PASS, and LIST ACTIVE. Changed connection hooks for FTP, SMTP, POP3, and NNTP classes to force use of an 8-bit US-ASCII superset (ISO-8859-1) for protocol communication. This was necessary because InputStreamReader and OutputStreamWriter use the default client-side character set encoding. fasselin@ca.ibm.com reported failure of SMTP on OS/390 which has EBCDIC as the native character set. Applied variation of fix suggested by Matthieu Recouly matthieu.recouly@laposte.net so that UnixFTPEntryParser may handle listings of the form: "drwxr-xr-x 1 usernameftp 512 Jan 29 23:32 prog" where the space between user name and group is omitted. Applied patch from Stephane Este-Gracias sestegra@free.fr that fixes the parsing of VMS listings by VMSFTPEntryParser.. If the buffer queue run full, the run() method sometimes hangs forever. Changed wait() to wait(100) as with other changes in TelnetInputStream. Fix submitted From: J. Matysiak ( j.matysiak@cenit.de ). FTP.smnt(String dir) was not passing on the dir to the SMNT command as an argument. Added a link to the FAQ currently hosted on the Apache Wiki. Changed package private NNTP._reader and NNTP._writer member variables to protected NNTP._reader_ and NNTP._writer_ variables as suggested by issue report 16995 to facilitate extending NNTPClient functionality in subclasses. Changed name of FTPClient.__openDataConnection() to FTPClient._openDataConnection_() to remain consistent with the convention in the code that protected members are of the form _foo_. At some point __openDataConnection() had been changed from private to protected. Added terminal option support to the telnet client with tests. From Bruno D'Avanzo ( b.davanzo@inwind.it ). New parsers merged with mainline with support for old list parsers. Added a migration document for moving from NetComponents to Commons/Net. Moved the ftp2 tree with tests to a proposal directory and setup a build for that code. This can grow in this area so users don't think it is production ready. Cleaned up license header on some source. Moved .io and .util to .net.io and .net.util in preparation for 1.0 release. Fixed typo in NNTP.removeProtocolCommandListener() method name. It was missing an L. From: joev@atg.com. Various site updates including this changes doc and publish date information. Patch for restarting FTP file transfers. The offset was not being sent immediately before the data transfer command on account. The bug was apparently introduced in NetComponents when it was decided to always send a PORT command before each data transfer to avoid socket reuse problems on Windows. From: Tapan Karecha ( tapan@india.hp.com ). Applied a fix for potential deadlock in TelnetInputStream by changing a wait() to a wait(100). From: Tapan Karecha ( tapan@india.hp.com ). FTP examples now use passive ftp connections. commons-net-rel-commons-net-3.9.0/src/changes/release-notes.vm000066400000000000000000000075731434047722200243310ustar00rootroot00000000000000## Licensed to the Apache Software Foundation (ASF) under one ## or more contributor license agreements. See the NOTICE file ## distributed with this work for additional information ## regarding copyright ownership. The ASF licenses this file ## to you under the Apache License, Version 2.0 (the ## "License"); you may not use this file except in compliance ## with the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, ## software distributed under the License is distributed on an ## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ## KIND, either express or implied. See the License for the ## specific language governing permissions and limitations ## under the License. ## ${project.name} ${version} RELEASE NOTES The ${developmentTeam} is pleased to announce the release of ${project.name} ${version}. $introduction.replaceAll("(? * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket. * * @throws SocketException If the socket could not be opened or the timeout could not be set. */ public void open() throws SocketException { _socket_ = _socketFactory_.createDatagramSocket(); _socket_.setSoTimeout(_timeout_); _isOpen_ = true; } /** * Opens a DatagramSocket on the local host at a specified port. Also sets the timeout on the socket to the default timeout set by {@link #setDefaultTimeout * setDefaultTimeout() }. *

* _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket. * * @param port The port to use for the socket. * @throws SocketException If the socket could not be opened or the timeout could not be set. */ public void open(final int port) throws SocketException { _socket_ = _socketFactory_.createDatagramSocket(port); _socket_.setSoTimeout(_timeout_); _isOpen_ = true; } /** * Opens a DatagramSocket at the specified address on the local host at a specified port. Also sets the timeout on the socket to the default timeout set by * {@link #setDefaultTimeout setDefaultTimeout() }. *

* _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket. * * @param port The port to use for the socket. * @param laddr The local address to use. * @throws SocketException If the socket could not be opened or the timeout could not be set. */ public void open(final int port, final InetAddress laddr) throws SocketException { _socket_ = _socketFactory_.createDatagramSocket(port, laddr); _socket_.setSoTimeout(_timeout_); _isOpen_ = true; } /** * Sets the charset. * * @param charset the charset. * @since 3.3 */ public void setCharset(final Charset charset) { this.charset = charset; } /** * Sets the DatagramSocketFactory used by the DatagramSocketClient to open DatagramSockets. If the factory value is null, then a default factory is used * (only do this to reset the factory after having previously altered it). * * @param factory The new DatagramSocketFactory the DatagramSocketClient should use. */ public void setDatagramSocketFactory(final DatagramSocketFactory factory) { if (factory == null) { _socketFactory_ = DEFAULT_SOCKET_FACTORY; } else { _socketFactory_ = factory; } } /** * Set the default timeout in milliseconds to use when opening a socket. After a call to open, the timeout for the socket is set using this value. This * method should be used prior to a call to {@link #open open()} and should not be confused with {@link #setSoTimeout setSoTimeout()} which operates on the * currently open socket. _timeout_ contains the new timeout value. * * @param timeout The timeout in milliseconds to use for the datagram socket connection. */ public void setDefaultTimeout(final int timeout) { _timeout_ = timeout; } /** * Set the timeout in milliseconds of a currently open connection. Only call this method after a connection has been opened by {@link #open open()}. * * @param timeout The timeout in milliseconds to use for the currently open datagram socket connection. * @throws SocketException if an error setting the timeout */ public void setSoTimeout(final int timeout) throws SocketException { _socket_.setSoTimeout(timeout); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/DatagramSocketFactory.java000066400000000000000000000045451434047722200330050ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; /** * The DatagramSocketFactory interface provides a means for the programmer to control the creation of datagram sockets and provide his own DatagramSocket * implementations for use by all classes derived from {@link org.apache.commons.net.DatagramSocketClient} . This allows you to provide your own DatagramSocket * implementations and to perform security checks or browser capability requests before creating a DatagramSocket. * * */ public interface DatagramSocketFactory { /** * Creates a DatagramSocket on the local host at the first available port. * * @return the socket * * @throws SocketException If the socket could not be created. */ DatagramSocket createDatagramSocket() throws SocketException; /** * Creates a DatagramSocket on the local host at a specified port. * * @param port The port to use for the socket. * @return the socket * @throws SocketException If the socket could not be created. */ DatagramSocket createDatagramSocket(int port) throws SocketException; /** * Creates a DatagramSocket at the specified address on the local host at a specified port. * * @param port The port to use for the socket. * @param laddr The local address to use. * @return the socket * @throws SocketException If the socket could not be created. */ DatagramSocket createDatagramSocket(int port, InetAddress laddr) throws SocketException; } DefaultDatagramSocketFactory.java000066400000000000000000000051141434047722200342240ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; /** * DefaultDatagramSocketFactory implements the DatagramSocketFactory interface by simply wrapping the java.net.DatagramSocket constructors. It is the default * DatagramSocketFactory used by {@link org.apache.commons.net.DatagramSocketClient} implementations. * * * @see DatagramSocketFactory * @see DatagramSocketClient * @see DatagramSocketClient#setDatagramSocketFactory */ public class DefaultDatagramSocketFactory implements DatagramSocketFactory { /** * Creates a DatagramSocket on the local host at the first available port. * * @return a new DatagramSocket * @throws SocketException If the socket could not be created. */ @Override public DatagramSocket createDatagramSocket() throws SocketException { return new DatagramSocket(); } /** * Creates a DatagramSocket on the local host at a specified port. * * @param port The port to use for the socket. * @return a new DatagramSocket * @throws SocketException If the socket could not be created. */ @Override public DatagramSocket createDatagramSocket(final int port) throws SocketException { return new DatagramSocket(port); } /** * Creates a DatagramSocket at the specified address on the local host at a specified port. * * @param port The port to use for the socket. * @param laddr The local address to use. * @return a new DatagramSocket * @throws SocketException If the socket could not be created. */ @Override public DatagramSocket createDatagramSocket(final int port, final InetAddress laddr) throws SocketException { return new DatagramSocket(port, laddr); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/DefaultSocketFactory.java000066400000000000000000000170761434047722200326540ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import javax.net.SocketFactory; /** * DefaultSocketFactory implements the SocketFactory interface by simply wrapping the java.net.Socket and java.net.ServerSocket constructors. It is the default * SocketFactory used by {@link org.apache.commons.net.SocketClient} implementations. * * * @see SocketFactory * @see SocketClient * @see SocketClient#setSocketFactory */ public class DefaultSocketFactory extends SocketFactory { /** The proxy to use when creating new sockets. */ private final Proxy connProxy; /** * The default constructor. */ public DefaultSocketFactory() { this(null); } /** * A constructor for sockets with proxy support. * * @param proxy The Proxy to use when creating new Sockets. * @since 3.2 */ public DefaultSocketFactory(final Proxy proxy) { connProxy = proxy; } /** * Creates a ServerSocket bound to a specified port. A port of 0 will create the ServerSocket on a system-determined free port. * * @param port The port on which to listen, or 0 to use any free port. * @return A ServerSocket that will listen on a specified port. * @throws IOException If an I/O error occurs while creating the ServerSocket. */ public ServerSocket createServerSocket(final int port) throws IOException { return new ServerSocket(port); } /** * Creates a ServerSocket bound to a specified port with a given maximum queue length for incoming connections. A port of 0 will create the ServerSocket on * a system-determined free port. * * @param port The port on which to listen, or 0 to use any free port. * @param backlog The maximum length of the queue for incoming connections. * @return A ServerSocket that will listen on a specified port. * @throws IOException If an I/O error occurs while creating the ServerSocket. */ public ServerSocket createServerSocket(final int port, final int backlog) throws IOException { return new ServerSocket(port, backlog); } /** * Creates a ServerSocket bound to a specified port on a given local address with a given maximum queue length for incoming connections. A port of 0 will * create the ServerSocket on a system-determined free port. * * @param port The port on which to listen, or 0 to use any free port. * @param backlog The maximum length of the queue for incoming connections. * @param bindAddr The local address to which the ServerSocket should bind. * @return A ServerSocket that will listen on a specified port. * @throws IOException If an I/O error occurs while creating the ServerSocket. */ public ServerSocket createServerSocket(final int port, final int backlog, final InetAddress bindAddr) throws IOException { return new ServerSocket(port, backlog, bindAddr); } /** * Creates an unconnected Socket. * * @return A new unconnected Socket. * @throws IOException If an I/O error occurs while creating the Socket. * @since 3.2 */ @Override public Socket createSocket() throws IOException { if (connProxy != null) { return new Socket(connProxy); } return new Socket(); } /** * Creates a Socket connected to the given host and port. * * @param address The address of the host to connect to. * @param port The port to connect to. * @return A Socket connected to the given host and port. * @throws IOException If an I/O error occurs while creating the Socket. */ @Override public Socket createSocket(final InetAddress address, final int port) throws IOException { if (connProxy != null) { final Socket s = new Socket(connProxy); s.connect(new InetSocketAddress(address, port)); return s; } return new Socket(address, port); } /** * Creates a Socket connected to the given host and port and originating from the specified local address and port. * * @param address The address of the host to connect to. * @param port The port to connect to. * @param localAddr The local address to use. * @param localPort The local port to use. * @return A Socket connected to the given host and port. * @throws IOException If an I/O error occurs while creating the Socket. */ @Override public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddr, final int localPort) throws IOException { if (connProxy != null) { final Socket s = new Socket(connProxy); s.bind(new InetSocketAddress(localAddr, localPort)); s.connect(new InetSocketAddress(address, port)); return s; } return new Socket(address, port, localAddr, localPort); } /** * Creates a Socket connected to the given host and port. * * @param host The hostname to connect to. * @param port The port to connect to. * @return A Socket connected to the given host and port. * @throws UnknownHostException If the hostname cannot be resolved. * @throws IOException If an I/O error occurs while creating the Socket. */ @Override public Socket createSocket(final String host, final int port) throws UnknownHostException, IOException { if (connProxy != null) { final Socket s = new Socket(connProxy); s.connect(new InetSocketAddress(host, port)); return s; } return new Socket(host, port); } /** * Creates a Socket connected to the given host and port and originating from the specified local address and port. * * @param host The hostname to connect to. * @param port The port to connect to. * @param localAddr The local address to use. * @param localPort The local port to use. * @return A Socket connected to the given host and port. * @throws UnknownHostException If the hostname cannot be resolved. * @throws IOException If an I/O error occurs while creating the Socket. */ @Override public Socket createSocket(final String host, final int port, final InetAddress localAddr, final int localPort) throws UnknownHostException, IOException { if (connProxy != null) { final Socket s = new Socket(connProxy); s.bind(new InetSocketAddress(localAddr, localPort)); s.connect(new InetSocketAddress(host, port)); return s; } return new Socket(host, port, localAddr, localPort); } } MalformedServerReplyException.java000066400000000000000000000036631434047722200344750ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.io.IOException; /** * This exception is used to indicate that the reply from a server could not be interpreted. Most of the NetComponents classes attempt to be as lenient as * possible when receiving server replies. Many server implementations deviate from IETF protocol specifications, making it necessary to be as flexible as * possible. However, there will be certain situations where it is not possible to continue an operation because the server reply could not be interpreted in a * meaningful manner. In these cases, a MalformedServerReplyException should be thrown. * * */ public class MalformedServerReplyException extends IOException { private static final long serialVersionUID = 6006765264250543945L; /** Constructs a MalformedServerReplyException with no message */ public MalformedServerReplyException() { } /** * Constructs a MalformedServerReplyException with a specified message. * * @param message The message explaining the reason for the exception. */ public MalformedServerReplyException(final String message) { super(message); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/PrintCommandListener.java000066400000000000000000000162071434047722200326630ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.io.PrintStream; import java.io.PrintWriter; /** * This is a support class for some of the example programs. It is a sample implementation of the ProtocolCommandListener interface which just prints out to a * specified stream all command/reply traffic. * * @since 2.0 */ public class PrintCommandListener implements ProtocolCommandListener { private final PrintWriter writer; private final boolean nologin; private final char eolMarker; private final boolean directionMarker; /** * Create the default instance which prints everything. * * @param stream where to write the commands and responses e.g. System.out * @since 3.0 */ public PrintCommandListener(final PrintStream stream) { this(new PrintWriter(stream)); } /** * Create an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character. * * @param stream where to write the commands and responses * @param suppressLogin if {@code true}, only print command name for login * * @since 3.0 */ public PrintCommandListener(final PrintStream stream, final boolean suppressLogin) { this(new PrintWriter(stream), suppressLogin); } /** * Create an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character. * * @param stream where to write the commands and responses * @param suppressLogin if {@code true}, only print command name for login * @param eolMarker if non-zero, add a marker just before the EOL. * * @since 3.0 */ public PrintCommandListener(final PrintStream stream, final boolean suppressLogin, final char eolMarker) { this(new PrintWriter(stream), suppressLogin, eolMarker); } /** * Create an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character. * * @param stream where to write the commands and responses * @param suppressLogin if {@code true}, only print command name for login * @param eolMarker if non-zero, add a marker just before the EOL. * @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the output * * @since 3.0 */ public PrintCommandListener(final PrintStream stream, final boolean suppressLogin, final char eolMarker, final boolean showDirection) { this(new PrintWriter(stream), suppressLogin, eolMarker, showDirection); } /** * Create the default instance which prints everything. * * @param writer where to write the commands and responses */ public PrintCommandListener(final PrintWriter writer) { this(writer, false); } /** * Create an instance which optionally suppresses login command text. * * @param writer where to write the commands and responses * @param suppressLogin if {@code true}, only print command name for login * * @since 3.0 */ public PrintCommandListener(final PrintWriter writer, final boolean suppressLogin) { this(writer, suppressLogin, (char) 0); } /** * Create an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character. * * @param writer where to write the commands and responses * @param suppressLogin if {@code true}, only print command name for login * @param eolMarker if non-zero, add a marker just before the EOL. * * @since 3.0 */ public PrintCommandListener(final PrintWriter writer, final boolean suppressLogin, final char eolMarker) { this(writer, suppressLogin, eolMarker, false); } /** * Create an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character. * * @param writer where to write the commands and responses * @param suppressLogin if {@code true}, only print command name for login * @param eolMarker if non-zero, add a marker just before the EOL. * @param showDirection if {@code true}, add {@code ">} " or {@code "< "} as appropriate to the output * * @since 3.0 */ public PrintCommandListener(final PrintWriter writer, final boolean suppressLogin, final char eolMarker, final boolean showDirection) { this.writer = writer; this.nologin = suppressLogin; this.eolMarker = eolMarker; this.directionMarker = showDirection; } private String getPrintableString(final String msg) { if (eolMarker == 0) { return msg; } final int pos = msg.indexOf(SocketClient.NETASCII_EOL); if (pos > 0) { final StringBuilder sb = new StringBuilder(); sb.append(msg.substring(0, pos)); sb.append(eolMarker); sb.append(msg.substring(pos)); return sb.toString(); } return msg; } @Override public void protocolCommandSent(final ProtocolCommandEvent event) { if (directionMarker) { writer.print("> "); } if (nologin) { final String cmd = event.getCommand(); if ("PASS".equalsIgnoreCase(cmd) || "USER".equalsIgnoreCase(cmd)) { writer.print(cmd); writer.println(" *******"); // Don't bother with EOL marker for this! } else { final String IMAP_LOGIN = "LOGIN"; if (IMAP_LOGIN.equalsIgnoreCase(cmd)) { // IMAP String msg = event.getMessage(); msg = msg.substring(0, msg.indexOf(IMAP_LOGIN) + IMAP_LOGIN.length()); writer.print(msg); writer.println(" *******"); // Don't bother with EOL marker for this! } else { writer.print(getPrintableString(event.getMessage())); } } } else { writer.print(getPrintableString(event.getMessage())); } writer.flush(); } @Override public void protocolReplyReceived(final ProtocolCommandEvent event) { if (directionMarker) { writer.print("< "); } writer.print(event.getMessage()); writer.flush(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java000066400000000000000000000114031434047722200326550ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.util.EventObject; /** * There exists a large class of IETF protocols that work by sending an ASCII text command and arguments to a server, and then receiving an ASCII text reply. * For debugging and other purposes, it is extremely useful to log or keep track of the contents of the protocol messages. The ProtocolCommandEvent class * coupled with the {@link org.apache.commons.net.ProtocolCommandListener} interface facilitate this process. * * * @see ProtocolCommandListener * @see ProtocolCommandSupport */ public class ProtocolCommandEvent extends EventObject { private static final long serialVersionUID = 403743538418947240L; private final int replyCode; private final boolean isCommand; private final String message, command; /** * Creates a ProtocolCommandEvent signalling a reply to a command was received. ProtocolCommandEvents created with this constructor should only be sent * after a complete command reply has been received fromt a server. * * @param source The source of the event. * @param replyCode The integer code indicating the natureof the reply. This will be the protocol integer value for protocols that use integer reply codes, * or the reply class constant corresponding to the reply for protocols like POP3 that use strings like OK rather than integer codes (i.e., * POP3Repy.OK). * @param message The entire reply as received from the server. */ public ProtocolCommandEvent(final Object source, final int replyCode, final String message) { super(source); this.replyCode = replyCode; this.message = message; this.isCommand = false; this.command = null; } /** * Creates a ProtocolCommandEvent signalling a command was sent to the server. ProtocolCommandEvents created with this constructor should only be sent after * a command has been sent, but before the reply has been received. * * @param source The source of the event. * @param command The string representation of the command type sent, not including the arguments (e.g., "STAT" or "GET"). * @param message The entire command string verbatim as sent to the server, including all arguments. */ public ProtocolCommandEvent(final Object source, final String command, final String message) { super(source); this.replyCode = 0; this.message = message; this.isCommand = true; this.command = command; } /** * Returns the string representation of the command type sent (e.g., "STAT" or "GET"). If the ProtocolCommandEvent is a reply event, then null is returned. * * @return The string representation of the command type sent, or null if this is a reply event. */ public String getCommand() { return command; } /** * Returns the entire message sent to or received from the server. Includes the line terminator. * * @return The entire message sent to or received from the server. */ public String getMessage() { return message; } /** * Returns the reply code of the received server reply. Undefined if this is not a reply event. * * @return The reply code of the received server reply. Undefined if not a reply event. */ public int getReplyCode() { return replyCode; } /** * Returns true if the ProtocolCommandEvent was generated as a result of sending a command. * * @return true If the ProtocolCommandEvent was generated as a result of sending a command. False otherwise. */ public boolean isCommand() { return isCommand; } /** * Returns true if the ProtocolCommandEvent was generated as a result of receiving a reply. * * @return true If the ProtocolCommandEvent was generated as a result of receiving a reply. False otherwise. */ public boolean isReply() { return !isCommand(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ProtocolCommandListener.java000066400000000000000000000041151434047722200333630ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.util.EventListener; /** * There exists a large class of IETF protocols that work by sending an ASCII text command and arguments to a server, and then receiving an ASCII text reply. * For debugging and other purposes, it is extremely useful to log or keep track of the contents of the protocol messages. The ProtocolCommandListener interface * coupled with the {@link ProtocolCommandEvent} class facilitate this process. *

* To receive ProtocolCommandEvents, you merely implement the ProtocolCommandListener interface and register the class as a listener with a ProtocolCommandEvent * source such as {@link org.apache.commons.net.ftp.FTPClient}. * * * @see ProtocolCommandEvent * @see ProtocolCommandSupport */ public interface ProtocolCommandListener extends EventListener { /** * This method is invoked by a ProtocolCommandEvent source after sending a protocol command to a server. * * @param event The ProtocolCommandEvent fired. */ void protocolCommandSent(ProtocolCommandEvent event); /** * This method is invoked by a ProtocolCommandEvent source after receiving a reply from a server. * * @param event The ProtocolCommandEvent fired. */ void protocolReplyReceived(ProtocolCommandEvent event); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java000066400000000000000000000115401434047722200332520ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.io.Serializable; import java.util.EventListener; import org.apache.commons.net.util.ListenerList; /** * ProtocolCommandSupport is a convenience class for managing a list of ProtocolCommandListeners and firing ProtocolCommandEvents. You can simply delegate * ProtocolCommandEvent firing and listener registering/unregistering tasks to this class. * * * @see ProtocolCommandEvent * @see ProtocolCommandListener */ public class ProtocolCommandSupport implements Serializable { private static final long serialVersionUID = -8017692739988399978L; private final Object source; private final ListenerList listeners; /** * Creates a ProtocolCommandSupport instance using the indicated source as the source of ProtocolCommandEvents. * * @param source The source to use for all generated ProtocolCommandEvents. */ public ProtocolCommandSupport(final Object source) { this.listeners = new ListenerList(); this.source = source; } /** * Adds a ProtocolCommandListener. * * @param listener The ProtocolCommandListener to add. */ public void addProtocolCommandListener(final ProtocolCommandListener listener) { listeners.addListener(listener); } /** * Fires a ProtocolCommandEvent signalling the sending of a command to all registered listeners, invoking their * {@link org.apache.commons.net.ProtocolCommandListener#protocolCommandSent protocolCommandSent() } methods. * * @param command The string representation of the command type sent, not including the arguments (e.g., "STAT" or "GET"). * @param message The entire command string verbatim as sent to the server, including all arguments. */ public void fireCommandSent(final String command, final String message) { final ProtocolCommandEvent event; event = new ProtocolCommandEvent(source, command, message); for (final EventListener listener : listeners) { ((ProtocolCommandListener) listener).protocolCommandSent(event); } } /** * Fires a ProtocolCommandEvent signalling the reception of a command reply to all registered listeners, invoking their * {@link org.apache.commons.net.ProtocolCommandListener#protocolReplyReceived protocolReplyReceived() } methods. * * @param replyCode The integer code indicating the natureof the reply. This will be the protocol integer value for protocols that use integer reply codes, * or the reply class constant corresponding to the reply for protocols like POP3 that use strings like OK rather than integer codes (i.e., * POP3Repy.OK). * @param message The entire reply as received from the server. */ public void fireReplyReceived(final int replyCode, final String message) { final ProtocolCommandEvent event; event = new ProtocolCommandEvent(source, replyCode, message); for (final EventListener listener : listeners) { ((ProtocolCommandListener) listener).protocolReplyReceived(event); } } /** * Returns the number of ProtocolCommandListeners currently registered. * * @return The number of ProtocolCommandListeners currently registered. */ public int getListenerCount() { return listeners.getListenerCount(); } private void readObject(final java.io.ObjectInputStream in) { throw new UnsupportedOperationException("Serialization is not supported"); } /* * Serialization is unnecessary for this class. Reject attempts to do so until such time as the Serializable attribute can be dropped. */ /** * Removes a ProtocolCommandListener. * * @param listener The ProtocolCommandListener to remove. */ public void removeProtocolCommandListener(final ProtocolCommandListener listener) { listeners.removeListener(listener); } private void writeObject(final java.io.ObjectOutputStream out) { throw new UnsupportedOperationException("Serialization is not supported"); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/SocketClient.java000066400000000000000000000720431434047722200311510ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; import java.net.SocketException; import java.nio.charset.Charset; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; /** * The SocketClient provides the basic operations that are required of client objects accessing sockets. It is meant to be subclassed to avoid having to rewrite * the same code over and over again to open a socket, close a socket, set timeouts, etc. Of special note is the {@link #setSocketFactory setSocketFactory } * method, which allows you to control the type of Socket the SocketClient creates for initiating network connections. This is especially useful for adding SSL * or proxy support as well as better support for applets. For example, you could create a {@link javax.net.SocketFactory} that requests browser security * capabilities before creating a socket. All classes derived from SocketClient should use the {@link #_socketFactory_ _socketFactory_ } member variable to * create Socket and ServerSocket instances rather than instantiating them by directly invoking a constructor. By honoring this contract you guarantee that a * user will always be able to provide his own Socket implementations by substituting his own SocketFactory. * * @see SocketFactory */ public abstract class SocketClient { /** * The end of line character sequence used by most IETF protocols. That is a carriage return followed by a newline: "\r\n" */ public static final String NETASCII_EOL = "\r\n"; /** The default SocketFactory shared by all SocketClient instances. */ private static final SocketFactory DEFAULT_SOCKET_FACTORY = SocketFactory.getDefault(); /** The default {@link ServerSocketFactory} */ private static final ServerSocketFactory DEFAULT_SERVER_SOCKET_FACTORY = ServerSocketFactory.getDefault(); /** The socket's connect timeout (0 = infinite timeout) */ private static final int DEFAULT_CONNECT_TIMEOUT = 60000; /** * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and the firing of ProtocolCommandEvents. */ private ProtocolCommandSupport commandSupport; /** The timeout to use after opening a socket. */ protected int _timeout_; /** The socket used for the connection. */ protected Socket _socket_; /** The hostname used for the connection (null = no hostname supplied). */ protected String _hostname_; /** The default port the client should connect to. */ protected int _defaultPort_; /** The socket's InputStream. */ protected InputStream _input_; /** The socket's OutputStream. */ protected OutputStream _output_; /** The socket's SocketFactory. */ protected SocketFactory _socketFactory_; /** The socket's ServerSocket Factory. */ protected ServerSocketFactory _serverSocketFactory_; protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT; /** Hint for SO_RCVBUF size */ private int receiveBufferSize = -1; /** Hint for SO_SNDBUF size */ private int sendBufferSize = -1; /** The proxy to use when connecting. */ private Proxy connProxy; /** * Charset to use for byte IO. */ private Charset charset = Charset.defaultCharset(); /** * Default constructor for SocketClient. Initializes _socket_ to null, _timeout_ to 0, _defaultPort to 0, _isConnected_ to false, charset to * {@code Charset.defaultCharset()} and _socketFactory_ to a shared instance of {@link org.apache.commons.net.DefaultSocketFactory}. */ public SocketClient() { _socket_ = null; _hostname_ = null; _input_ = null; _output_ = null; _timeout_ = 0; _defaultPort_ = 0; _socketFactory_ = DEFAULT_SOCKET_FACTORY; _serverSocketFactory_ = DEFAULT_SERVER_SOCKET_FACTORY; } // helper method to allow code to be shared with connect(String,...) methods private void _connect(final InetAddress host, final int port, final InetAddress localAddr, final int localPort) throws SocketException, IOException { _socket_ = _socketFactory_.createSocket(); if (receiveBufferSize != -1) { _socket_.setReceiveBufferSize(receiveBufferSize); } if (sendBufferSize != -1) { _socket_.setSendBufferSize(sendBufferSize); } if (localAddr != null) { _socket_.bind(new InetSocketAddress(localAddr, localPort)); } _socket_.connect(new InetSocketAddress(host, port), connectTimeout); _connectAction_(); } /** * Because there are so many connect() methods, the _connectAction_() method is provided as a means of performing some action immediately after establishing * a connection, rather than reimplementing all of the connect() methods. The last action performed by every connect() method after opening a socket is to * call this method. *

* This method sets the timeout on the just opened socket to the default timeout set by {@link #setDefaultTimeout setDefaultTimeout() }, sets _input_ and * _output_ to the socket's InputStream and OutputStream respectively, and sets _isConnected_ to true. *

* Subclasses overriding this method should start by calling super._connectAction_() first to ensure the initialization of the aforementioned * protected variables. * * @throws IOException (SocketException) if a problem occurs with the socket */ protected void _connectAction_() throws IOException { applySocketAttributes(); _input_ = _socket_.getInputStream(); _output_ = _socket_.getOutputStream(); } /** * Adds a ProtocolCommandListener. * * @param listener The ProtocolCommandListener to add. * @since 3.0 */ public void addProtocolCommandListener(final ProtocolCommandListener listener) { getCommandSupport().addProtocolCommandListener(listener); } /** * Applies socket attributes. * * @throws SocketException if there is an error in the underlying protocol, such as a TCP error. * @since 3.8.0 */ protected void applySocketAttributes() throws SocketException { _socket_.setSoTimeout(_timeout_); } private void closeQuietly(final Closeable close) { if (close != null) { try { close.close(); } catch (final IOException e) { // Ignored } } } private void closeQuietly(final Socket socket) { if (socket != null) { try { socket.close(); } catch (final IOException e) { // Ignored } } } /** * Opens a Socket connected to a remote host at the current default port and originating from the current host at a system assigned port. Before returning, * {@link #_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param host The remote host. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is derived from * it. */ public void connect(final InetAddress host) throws SocketException, IOException { _hostname_ = null; connect(host, _defaultPort_); } /** * Opens a Socket connected to a remote host at the specified port and originating from the current host at a system assigned port. Before returning, * {@link #_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param host The remote host. * @param port The port to connect to on the remote host. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is derived from * it. */ public void connect(final InetAddress host, final int port) throws SocketException, IOException { _hostname_ = null; _connect(host, port, null, -1); } /** * Opens a Socket connected to a remote host at the specified port and originating from the specified local address and port. Before returning, * {@link #_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param host The remote host. * @param port The port to connect to on the remote host. * @param localAddr The local address to use. * @param localPort The local port to use. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is derived from * it. */ public void connect(final InetAddress host, final int port, final InetAddress localAddr, final int localPort) throws SocketException, IOException { _hostname_ = null; _connect(host, port, localAddr, localPort); } /** * Opens a Socket connected to a remote host at the current default port and originating from the current host at a system assigned port. Before returning, * {@link #_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param hostname The name of the remote host. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is * derived from it. * @throws java.net.UnknownHostException If the hostname cannot be resolved. */ public void connect(final String hostname) throws SocketException, IOException { connect(hostname, _defaultPort_); } /** * Opens a Socket connected to a remote host at the specified port and originating from the current host at a system assigned port. Before returning, * {@link #_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param hostname The name of the remote host. * @param port The port to connect to on the remote host. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is * derived from it. * @throws java.net.UnknownHostException If the hostname cannot be resolved. */ public void connect(final String hostname, final int port) throws SocketException, IOException { _hostname_ = hostname; _connect(InetAddress.getByName(hostname), port, null, -1); } /** * Opens a Socket connected to a remote host at the specified port and originating from the specified local address and port. Before returning, * {@link #_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param hostname The name of the remote host. * @param port The port to connect to on the remote host. * @param localAddr The local address to use. * @param localPort The local port to use. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is * derived from it. * @throws java.net.UnknownHostException If the hostname cannot be resolved. */ public void connect(final String hostname, final int port, final InetAddress localAddr, final int localPort) throws SocketException, IOException { _hostname_ = hostname; _connect(InetAddress.getByName(hostname), port, localAddr, localPort); } /** * Create the CommandSupport instance if required */ protected void createCommandSupport() { commandSupport = new ProtocolCommandSupport(this); } /** * Disconnects the socket connection. You should call this method after you've finished using the class instance and also before you call {@link #connect * connect() } again. _isConnected_ is set to false, _socket_ is set to null, _input_ is set to null, and _output_ is set to null. * * @throws IOException If there is an error closing the socket. */ public void disconnect() throws IOException { closeQuietly(_socket_); closeQuietly(_input_); closeQuietly(_output_); _socket_ = null; _hostname_ = null; _input_ = null; _output_ = null; } /** * If there are any listeners, send them the command details. * * @param command the command name * @param message the complete message, including command name * @since 3.0 */ protected void fireCommandSent(final String command, final String message) { if (getCommandSupport().getListenerCount() > 0) { getCommandSupport().fireCommandSent(command, message); } } /** * If there are any listeners, send them the reply details. * * @param replyCode the code extracted from the reply * @param reply the full reply text * @since 3.0 */ protected void fireReplyReceived(final int replyCode, final String reply) { if (getCommandSupport().getListenerCount() > 0) { getCommandSupport().fireReplyReceived(replyCode, reply); } } /** * Gets the charset. * * @return the charset. * @since 3.3 */ public Charset getCharset() { return charset; } /** * Gets the charset name. * * @return the charset. * @since 3.3 * @deprecated Since the code now requires Java 1.6 as a mininmum */ @Deprecated public String getCharsetName() { return charset.name(); } /** * Subclasses can override this if they need to provide their own instance field for backwards compatibilty. * * @return the CommandSupport instance, may be {@code null} * @since 3.0 */ protected ProtocolCommandSupport getCommandSupport() { return commandSupport; } /** * Get the underlying socket connection timeout. * * @return timeout (in ms) * @since 2.0 */ public int getConnectTimeout() { return connectTimeout; } /** * Returns the current value of the default port (stored in {@link #_defaultPort_ _defaultPort_ }). * * @return The current value of the default port. */ public int getDefaultPort() { return _defaultPort_; } /** * Returns the default timeout in milliseconds that is used when opening a socket. * * @return The default timeout in milliseconds that is used when opening a socket. */ public int getDefaultTimeout() { return _timeout_; } /** * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket. Delegates to {@link Socket#getKeepAlive()} * * @return True if SO_KEEPALIVE is enabled. * @throws SocketException if there is a problem with the socket * @throws NullPointerException if the socket is not currently open * @since 2.2 */ public boolean getKeepAlive() throws SocketException { return _socket_.getKeepAlive(); } /** * Returns the local address to which the client's socket is bound. Delegates to {@link Socket#getLocalAddress()} * * @return The local address to which the client's socket is bound. * @throws NullPointerException if the socket is not currently open */ public InetAddress getLocalAddress() { return _socket_.getLocalAddress(); } /** * Returns the port number of the open socket on the local host used for the connection. Delegates to {@link Socket#getLocalPort()} * * @return The port number of the open socket on the local host used for the connection. * @throws NullPointerException if the socket is not currently open */ public int getLocalPort() { return _socket_.getLocalPort(); } /** * Gets the proxy for use with all the connections. * * @return the current proxy for connections. */ public Proxy getProxy() { return connProxy; } /** * Get the current receivedBuffer size * * @return the size, or -1 if not initialized * @since 3.0 */ protected int getReceiveBufferSize() { return receiveBufferSize; } /** * @return The remote address to which the client is connected. Delegates to {@link Socket#getInetAddress()} * @throws NullPointerException if the socket is not currently open */ public InetAddress getRemoteAddress() { return _socket_.getInetAddress(); } /** * Returns the port number of the remote host to which the client is connected. Delegates to {@link Socket#getPort()} * * @return The port number of the remote host to which the client is connected. * @throws NullPointerException if the socket is not currently open */ public int getRemotePort() { return _socket_.getPort(); } /** * Get the current sendBuffer size * * @return the size, or -1 if not initialized * @since 3.0 */ protected int getSendBufferSize() { return sendBufferSize; } /** * Get the underlying {@link ServerSocketFactory} * * @return The server socket factory * @since 2.2 */ public ServerSocketFactory getServerSocketFactory() { return _serverSocketFactory_; } /** * Returns the current SO_LINGER timeout of the currently opened socket. * * @return The current SO_LINGER timeout. If SO_LINGER is disabled returns -1. * @throws SocketException If the operation fails. * @throws NullPointerException if the socket is not currently open */ public int getSoLinger() throws SocketException { return _socket_.getSoLinger(); } /** * Returns the timeout in milliseconds of the currently opened socket. * * @return The timeout in milliseconds of the currently opened socket. * @throws SocketException If the operation fails. * @throws NullPointerException if the socket is not currently open */ public int getSoTimeout() throws SocketException { return _socket_.getSoTimeout(); } /** * Returns true if Nagle's algorithm is enabled on the currently opened socket. * * @return True if Nagle's algorithm is enabled on the currently opened socket, false otherwise. * @throws SocketException If the operation fails. * @throws NullPointerException if the socket is not currently open */ public boolean getTcpNoDelay() throws SocketException { return _socket_.getTcpNoDelay(); } /** * Make various checks on the socket to test if it is available for use. Note that the only sure test is to use it, but these checks may help in some cases. * * @see NET-350 * @return {@code true} if the socket appears to be available for use * @since 3.0 */ @SuppressWarnings("resource") public boolean isAvailable() { if (isConnected()) { try { if (_socket_.getInetAddress() == null) { return false; } if (_socket_.getPort() == 0) { return false; } if (_socket_.getRemoteSocketAddress() == null) { return false; } if (_socket_.isClosed()) { return false; } /* * these aren't exact checks (a Socket can be half-open), but since we usually require two-way data transfer, we check these here too: */ if (_socket_.isInputShutdown()) { return false; } if (_socket_.isOutputShutdown()) { return false; } /* ignore the result, catch exceptions: */ // No need to close _socket_.getInputStream(); // No need to close _socket_.getOutputStream(); } catch (final IOException ioex) { return false; } return true; } return false; } /** * Returns true if the client is currently connected to a server. * * Delegates to {@link Socket#isConnected()} * * @return True if the client is currently connected to a server, false otherwise. */ public boolean isConnected() { if (_socket_ == null) { return false; } return _socket_.isConnected(); } /** * Removes a ProtocolCommandListener. * * @param listener The ProtocolCommandListener to remove. * @since 3.0 */ public void removeProtocolCommandListener(final ProtocolCommandListener listener) { getCommandSupport().removeProtocolCommandListener(listener); } /** * Sets the charset. * * @param charset the charset. * @since 3.3 */ public void setCharset(final Charset charset) { this.charset = charset; } /** * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's connect() method. * * @param connectTimeout The connection timeout to use (in ms) * @since 2.0 */ public void setConnectTimeout(final int connectTimeout) { this.connectTimeout = connectTimeout; } /** * Sets the default port the SocketClient should connect to when a port is not specified. The {@link #_defaultPort_ _defaultPort_ } variable stores this * value. If never set, the default port is equal to zero. * * @param port The default port to set. */ public void setDefaultPort(final int port) { _defaultPort_ = port; } /** * Set the default timeout in milliseconds to use when opening a socket. This value is only used previous to a call to {@link #connect connect()} and should * not be confused with {@link #setSoTimeout setSoTimeout()} which operates on an the currently opened socket. _timeout_ contains the new timeout value. * * @param timeout The timeout in milliseconds to use for the socket connection. */ public void setDefaultTimeout(final int timeout) { _timeout_ = timeout; } /** * Sets the SO_KEEPALIVE flag on the currently opened socket. * * From the Javadocs, the default keepalive time is 2 hours (although this is implementation dependent). It looks as though the Windows WSA sockets * implementation allows a specific keepalive value to be set, although this seems not to be the case on other systems. * * @param keepAlive If true, keepAlive is turned on * @throws SocketException if there is a problem with the socket * @throws NullPointerException if the socket is not currently open * @since 2.2 */ public void setKeepAlive(final boolean keepAlive) throws SocketException { _socket_.setKeepAlive(keepAlive); } /** * Sets the proxy for use with all the connections. The proxy is used for connections established after the call to this method. * * @param proxy the new proxy for connections. * @since 3.2 */ public void setProxy(final Proxy proxy) { setSocketFactory(new DefaultSocketFactory(proxy)); connProxy = proxy; } /** * Sets the underlying socket receive buffer size. * * @param size The size of the buffer in bytes. * @throws SocketException never (but subclasses may wish to do so) * @since 2.0 */ public void setReceiveBufferSize(final int size) throws SocketException { receiveBufferSize = size; } /** * Set the underlying socket send buffer size. * * @param size The size of the buffer in bytes. * @throws SocketException never thrown, but subclasses might want to do so * @since 2.0 */ public void setSendBufferSize(final int size) throws SocketException { sendBufferSize = size; } /** * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket connections. If the factory value is null, then a default factory is used * (only do this to reset the factory after having previously altered it). * * @param factory The new ServerSocketFactory the SocketClient should use. * @since 2.0 */ public void setServerSocketFactory(final ServerSocketFactory factory) { if (factory == null) { _serverSocketFactory_ = DEFAULT_SERVER_SOCKET_FACTORY; } else { _serverSocketFactory_ = factory; } } /** * Sets the SocketFactory used by the SocketClient to open socket connections. If the factory value is null, then a default factory is used (only do this to * reset the factory after having previously altered it). Any proxy setting is discarded. * * @param factory The new SocketFactory the SocketClient should use. */ public void setSocketFactory(final SocketFactory factory) { if (factory == null) { _socketFactory_ = DEFAULT_SOCKET_FACTORY; } else { _socketFactory_ = factory; } } /** * Sets the SO_LINGER timeout on the currently opened socket. * * @param on True if linger is to be enabled, false if not. * @param val The linger timeout (in hundredths of a second?) * @throws SocketException If the operation fails. * @throws NullPointerException if the socket is not currently open */ public void setSoLinger(final boolean on, final int val) throws SocketException { _socket_.setSoLinger(on, val); } /** * Set the timeout in milliseconds of a currently open connection. Only call this method after a connection has been opened by {@link #connect connect()}. *

* To set the initial timeout, use {@link #setDefaultTimeout(int)} instead. * * @param timeout The timeout in milliseconds to use for the currently open socket connection. * @throws SocketException If the operation fails. * @throws NullPointerException if the socket is not currently open */ public void setSoTimeout(final int timeout) throws SocketException { _socket_.setSoTimeout(timeout); } /** * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the currently opened socket. * * @param on True if Nagle's algorithm is to be enabled, false if not. * @throws SocketException If the operation fails. * @throws NullPointerException if the socket is not currently open */ public void setTcpNoDelay(final boolean on) throws SocketException { _socket_.setTcpNoDelay(on); } /** * Verifies that the remote end of the given socket is connected to the the same host that the SocketClient is currently connected to. This is useful for * doing a quick security check when a client needs to accept a connection from a server, such as an FTP data connection or a BSD R command standard error * stream. * * @param socket the item to check against * @return True if the remote hosts are the same, false if not. */ public boolean verifyRemote(final Socket socket) { final InetAddress host1; final InetAddress host2; host1 = socket.getInetAddress(); host2 = getRemoteAddress(); return host1.equals(host2); } /* * N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility, so the abstract method is needed to pass the instance to the * methods which were moved here. */ } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/bsd/000077500000000000000000000000001434047722200264615ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/bsd/RCommandClient.java000066400000000000000000000406201434047722200321650ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.bsd; import java.io.IOException; import java.io.InputStream; import java.net.BindException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import org.apache.commons.net.io.SocketInputStream; /** * RCommandClient is very similar to {@link org.apache.commons.net.bsd.RExecClient}, from which it is derived, and implements the rcmd() facility that first * appeared in 4.2BSD Unix. rcmd() is the facility used by the rsh (rshell) and other commands to execute a command on another machine from a trusted host * without issuing a password. The trust relationship between two machines is established by the contents of a machine's /etc/hosts.equiv file and a user's * .rhosts file. These files specify from which hosts and accounts on those hosts rcmd() requests will be accepted. The only additional measure for establishing * trust is that all client connections must originate from a port between 512 and 1023. Consequently, there is an upper limit to the number of rcmd connections * that can be running simultaneously. The required ports are reserved ports on Unix systems, and can only be bound by a process running with root permissions * (to accomplish this rsh, rlogin, and related commands usualy have the suid bit set). Therefore, on a Unix system, you will only be able to successfully use * the RCommandClient class if the process runs as root. However, there is no such restriction on Windows95 and some other systems. The security risks are * obvious. However, when carefully used, rcmd() can be very useful when used behind a firewall. *

* As with virtually all of the client classes in org.apache.commons.net, this class derives from SocketClient. But it overrides most of its connection methods * so that the local Socket will originate from an acceptable rshell port. The way to use RCommandClient is to first connect to the server, call the * {@link #rcommand rcommand() } method, and then fetch the connection's input, output, and optionally error streams. Interaction with the remote command is * controlled entirely through the I/O streams. Once you have finished processing the streams, you should invoke * {@link org.apache.commons.net.bsd.RExecClient#disconnect disconnect() } to clean up properly. *

* By default the standard output and standard error streams of the remote process are transmitted over the same connection, readable from the input stream * returned by {@link org.apache.commons.net.bsd.RExecClient#getInputStream getInputStream() } . However, it is possible to tell the rshd daemon to return the * standard error stream over a separate connection, readable from the input stream returned by {@link org.apache.commons.net.bsd.RExecClient#getErrorStream * getErrorStream() } . You can specify that a separate connection should be created for standard error by setting the boolean * separateErrorStream parameter of {@link #rcommand rcommand() } to true . The standard input of the remote process can be written * to through the output stream returned by {@link org.apache.commons.net.bsd.RExecClient#getOutputStream getOutputStream() } . * * @see org.apache.commons.net.SocketClient * @see RExecClient * @see RLoginClient */ public class RCommandClient extends RExecClient { /** * The default rshell port. Set to 514 in BSD Unix. */ public static final int DEFAULT_PORT = 514; /** * The smallest port number an rcmd client may use. By BSD convention this number is 512. */ public static final int MIN_CLIENT_PORT = 512; /** * The largest port number an rcmd client may use. By BSD convention this number is 1023. */ public static final int MAX_CLIENT_PORT = 1023; /** * The default RCommandClient constructor. Initializes the default port to DEFAULT_PORT . */ public RCommandClient() { setDefaultPort(DEFAULT_PORT); } /** * Opens a Socket connected to a remote host at the specified port and originating from the current host at a port in a range acceptable to the BSD rshell * daemon. Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } is called to perform connection initialization * actions. * * @param host The remote host. * @param port The port to connect to on the remote host. * @throws SocketException If the socket timeout could not be set. * @throws BindException If all acceptable rshell ports are in use. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is derived from * it. */ @Override public void connect(final InetAddress host, final int port) throws SocketException, IOException { connect(host, port, InetAddress.getLocalHost()); } /** * Opens a Socket connected to a remote host at the specified port and originating from the specified local address using a port in a range acceptable to * the BSD rshell daemon. Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } is called to perform connection * initialization actions. * * @param host The remote host. * @param port The port to connect to on the remote host. * @param localAddr The local address to use. * @throws SocketException If the socket timeout could not be set. * @throws BindException If all acceptable rshell ports are in use. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is derived from * it. */ public void connect(final InetAddress host, final int port, final InetAddress localAddr) throws SocketException, BindException, IOException { int localPort; localPort = MAX_CLIENT_PORT; for (localPort = MAX_CLIENT_PORT; localPort >= MIN_CLIENT_PORT; --localPort) { try { _socket_ = _socketFactory_.createSocket(host, port, localAddr, localPort); } catch (final SocketException e) { continue; } break; } if (localPort < MIN_CLIENT_PORT) { throw new BindException("All ports in use or insufficient permssion."); } _connectAction_(); } /** * Opens a Socket connected to a remote host at the specified port and originating from the specified local address and port. The local port must lie * between MIN_CLIENT_PORT and MAX_CLIENT_PORT or an IllegalArgumentException will be thrown. Before returning, * {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param host The remote host. * @param port The port to connect to on the remote host. * @param localAddr The local address to use. * @param localPort The local port to use. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is * derived from it. * @throws IllegalArgumentException If an invalid local port number is specified. */ @Override public void connect(final InetAddress host, final int port, final InetAddress localAddr, final int localPort) throws SocketException, IOException, IllegalArgumentException { if (localPort < MIN_CLIENT_PORT || localPort > MAX_CLIENT_PORT) { throw new IllegalArgumentException("Invalid port number " + localPort); } super.connect(host, port, localAddr, localPort); } /** * Opens a Socket connected to a remote host at the specified port and originating from the current host at a port in a range acceptable to the BSD rshell * daemon. Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } is called to perform connection initialization * actions. * * @param hostname The name of the remote host. * @param port The port to connect to on the remote host. * @throws SocketException If the socket timeout could not be set. * @throws BindException If all acceptable rshell ports are in use. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is derived * from it. * @throws UnknownHostException If the hostname cannot be resolved. */ @Override public void connect(final String hostname, final int port) throws SocketException, IOException, UnknownHostException { connect(InetAddress.getByName(hostname), port, InetAddress.getLocalHost()); } /** * Opens a Socket connected to a remote host at the specified port and originating from the specified local address using a port in a range acceptable to * the BSD rshell daemon. Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } is called to perform connection * initialization actions. * * @param hostname The remote host. * @param port The port to connect to on the remote host. * @param localAddr The local address to use. * @throws SocketException If the socket timeout could not be set. * @throws BindException If all acceptable rshell ports are in use. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is derived from * it. */ public void connect(final String hostname, final int port, final InetAddress localAddr) throws SocketException, IOException { connect(InetAddress.getByName(hostname), port, localAddr); } /** * Opens a Socket connected to a remote host at the specified port and originating from the specified local address and port. The local port must lie * between MIN_CLIENT_PORT and MAX_CLIENT_PORT or an IllegalArgumentException will be thrown. Before returning, * {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } is called to perform connection initialization actions. * * @param hostname The name of the remote host. * @param port The port to connect to on the remote host. * @param localAddr The local address to use. * @param localPort The local port to use. * @throws SocketException If the socket timeout could not be set. * @throws IOException If the socket could not be opened. In most cases you will only want to catch IOException since SocketException is * derived from it. * @throws UnknownHostException If the hostname cannot be resolved. * @throws IllegalArgumentException If an invalid local port number is specified. */ @Override public void connect(final String hostname, final int port, final InetAddress localAddr, final int localPort) throws SocketException, IOException, IllegalArgumentException, UnknownHostException { if (localPort < MIN_CLIENT_PORT || localPort > MAX_CLIENT_PORT) { throw new IllegalArgumentException("Invalid port number " + localPort); } super.connect(hostname, port, localAddr, localPort); } // Overrides method in RExecClient in order to implement proper // port number limitations. @Override InputStream createErrorStream() throws IOException { int localPort; final Socket socket; localPort = MAX_CLIENT_PORT; ServerSocket server = null; for (localPort = MAX_CLIENT_PORT; localPort >= MIN_CLIENT_PORT; --localPort) { try { server = _serverSocketFactory_.createServerSocket(localPort, 1, getLocalAddress()); break; // got a socket } catch (final SocketException e) { continue; } } if (server == null) { throw new BindException("All ports in use."); } _output_.write(Integer.toString(server.getLocalPort()).getBytes(StandardCharsets.UTF_8)); // $NON-NLS _output_.write(NULL_CHAR); _output_.flush(); socket = server.accept(); server.close(); if (isRemoteVerificationEnabled() && !verifyRemote(socket)) { socket.close(); throw new IOException("Security violation: unexpected connection attempt by " + socket.getInetAddress().getHostAddress()); } return new SocketInputStream(socket, socket.getInputStream()); } /** * Same as rcommand(localUsername, remoteUsername, command, false); * * @param localUsername the local user * @param remoteUsername the remote user * @param command the command * @throws IOException on error */ public void rcommand(final String localUsername, final String remoteUsername, final String command) throws IOException { rcommand(localUsername, remoteUsername, command, false); } /** * Remotely executes a command through the rshd daemon on the server to which the RCommandClient is connected. After calling this method, you may interact * with the remote process through its standard input, output, and error streams. You will typically be able to detect the termination of the remote process * after reaching end of file on its standard output (accessible through {@link #getInputStream getInputStream() }. Disconnecting from the server or closing * the process streams before reaching end of file will not necessarily terminate the remote process. *

* If a separate error stream is requested, the remote server will connect to a local socket opened by RCommandClient, providing an independent stream * through which standard error will be transmitted. The local socket must originate from a secure port (512 - 1023), and rcommand() ensures that this will * be so. RCommandClient will also do a simple security check when it accepts a connection for this error stream. If the connection does not originate from * the remote server, an IOException will be thrown. This serves as a simple protection against possible hijacking of the error stream by an attacker * monitoring the rexec() negotiation. You may disable this behavior with {@link org.apache.commons.net.bsd.RExecClient#setRemoteVerificationEnabled * setRemoteVerificationEnabled()} . *

* * @param localUsername The user account on the local machine that is requesting the command execution. * @param remoteUsername The account name on the server through which to execute the command. * @param command The command, including any arguments, to execute. * @param separateErrorStream True if you would like the standard error to be transmitted through a different stream than standard output. False if not. * @throws IOException If the rcommand() attempt fails. The exception will contain a message indicating the nature of the failure. */ public void rcommand(final String localUsername, final String remoteUsername, final String command, final boolean separateErrorStream) throws IOException { rexec(localUsername, remoteUsername, command, separateErrorStream); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/bsd/RExecClient.java000066400000000000000000000244751434047722200315050ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.bsd; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import org.apache.commons.net.SocketClient; import org.apache.commons.net.io.SocketInputStream; import org.apache.commons.net.util.NetConstants; /** * RExecClient implements the rexec() facility that first appeared in 4.2BSD Unix. This class will probably only be of use for connecting to Unix systems and * only when the rexecd daemon is configured to run, which is a rarity these days because of the security risks involved. However, rexec() can be very useful * for performing administrative tasks on a network behind a firewall. *

* As with virtually all of the client classes in org.apache.commons.net, this class derives from SocketClient, inheriting its connection methods. The way to * use RExecClient is to first connect to the server, call the {@link #rexec rexec()} method, and then fetch the connection's input, output, and optionally * error streams. Interaction with the remote command is controlled entirely through the I/O streams. Once you have finished processing the streams, you should * invoke {@link #disconnect disconnect()} to clean up properly. *

* By default the standard output and standard error streams of the remote process are transmitted over the same connection, readable from the input stream * returned by {@link #getInputStream getInputStream()}. However, it is possible to tell the rexecd daemon to return the standard error stream over a separate * connection, readable from the input stream returned by {@link #getErrorStream getErrorStream()}. You can specify that a separate connection should be created * for standard error by setting the boolean separateErrorStream parameter of {@link #rexec rexec()} to true . The standard input * of the remote process can be written to through the output stream returned by {@link #getOutputStream getOutputSream()}. * * @see SocketClient * @see RCommandClient * @see RLoginClient */ public class RExecClient extends SocketClient { /** * @since 3.3 */ protected static final char NULL_CHAR = '\0'; /** * The default rexec port. Set to 512 in BSD Unix. */ public static final int DEFAULT_PORT = 512; private boolean remoteVerificationEnabled; /** * If a separate error stream is requested, _errorStream_ will point to an InputStream from which the standard error of the remote process can * be read (after a call to rexec()). Otherwise, _errorStream_ will be null. */ protected InputStream _errorStream_; /** * The default RExecClient constructor. Initializes the default port to DEFAULT_PORT . */ public RExecClient() { _errorStream_ = null; setDefaultPort(DEFAULT_PORT); } // This can be overridden in local package to implement port range // limitations of rcmd and rlogin InputStream createErrorStream() throws IOException { final Socket socket; try (final ServerSocket server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress())) { _output_.write(Integer.toString(server.getLocalPort()).getBytes(StandardCharsets.UTF_8)); // $NON-NLS-1$ _output_.write(NULL_CHAR); _output_.flush(); socket = server.accept(); } if (remoteVerificationEnabled && !verifyRemote(socket)) { socket.close(); throw new IOException("Security violation: unexpected connection attempt by " + socket.getInetAddress().getHostAddress()); } return new SocketInputStream(socket, socket.getInputStream()); } /** * Disconnects from the server, closing all associated open sockets and streams. * * @throws IOException If there an error occurs while disconnecting. */ @Override public void disconnect() throws IOException { if (_errorStream_ != null) { _errorStream_.close(); } _errorStream_ = null; super.disconnect(); } /** * Returns the InputStream from which the standard error of the remote process can be read if a separate error stream is requested from the server. * Otherwise, null will be returned. The error stream will only be set after a successful rexec() invocation. * * @return The InputStream from which the standard error of the remote process can be read if a separate error stream is requested from the server. * Otherwise, null will be returned. */ public InputStream getErrorStream() { return _errorStream_; } /** * Returns the InputStream from which the standard output of the remote process can be read. The input stream will only be set after a successful rexec() * invocation. * * @return The InputStream from which the standard output of the remote process can be read. */ public InputStream getInputStream() { return _input_; } /** * Returns the OutputStream through which the standard input of the remote process can be written. The output stream will only be set after a successful * rexec() invocation. * * @return The OutputStream through which the standard input of the remote process can be written. */ public OutputStream getOutputStream() { return _output_; } /** * Return whether or not verification of the remote host providing a separate error stream is enabled. The default behavior is for verification to be * enabled. * * @return True if verification is enabled, false if not. */ public final boolean isRemoteVerificationEnabled() { return remoteVerificationEnabled; } /** * Same as rexec(username, password, command, false); * * @param username the user name * @param password the password * @param command the command to run * @throws IOException if an error occurs */ public void rexec(final String username, final String password, final String command) throws IOException { rexec(username, password, command, false); } /** * Remotely executes a command through the rexecd daemon on the server to which the RExecClient is connected. After calling this method, you may interact * with the remote process through its standard input, output, and error streams. You will typically be able to detect the termination of the remote process * after reaching end of file on its standard output (accessible through {@link #getInputStream getInputStream() }. Disconnecting from the server or closing * the process streams before reaching end of file will not necessarily terminate the remote process. *

* If a separate error stream is requested, the remote server will connect to a local socket opened by RExecClient, providing an independent stream through * which standard error will be transmitted. RExecClient will do a simple security check when it accepts a connection for this error stream. If the * connection does not originate from the remote server, an IOException will be thrown. This serves as a simple protection against possible hijacking of the * error stream by an attacker monitoring the rexec() negotiation. You may disable this behavior with {@link #setRemoteVerificationEnabled * setRemoteVerificationEnabled()} . * * @param username The account name on the server through which to execute the command. * @param password The plain text password of the user account. * @param command The command, including any arguments, to execute. * @param separateErrorStream True if you would like the standard error to be transmitted through a different stream than standard output. False if not. * @throws IOException If the rexec() attempt fails. The exception will contain a message indicating the nature of the failure. */ public void rexec(final String username, final String password, final String command, final boolean separateErrorStream) throws IOException { int ch; if (separateErrorStream) { _errorStream_ = createErrorStream(); } else { _output_.write(NULL_CHAR); } _output_.write(username.getBytes(getCharset())); _output_.write(NULL_CHAR); _output_.write(password.getBytes(getCharset())); _output_.write(NULL_CHAR); _output_.write(command.getBytes(getCharset())); _output_.write(NULL_CHAR); _output_.flush(); ch = _input_.read(); if (ch > 0) { final StringBuilder buffer = new StringBuilder(); while ((ch = _input_.read()) != NetConstants.EOS && ch != '\n') { buffer.append((char) ch); } throw new IOException(buffer.toString()); } if (ch < 0) { throw new IOException("Server closed connection."); } } /** * Enable or disable verification that the remote host connecting to create a separate error stream is the same as the host to which the standard out stream * is connected. The default is for verification to be enabled. You may set this value at any time, whether the client is currently connected or not. * * @param enable True to enable verification, false to disable verification. */ public final void setRemoteVerificationEnabled(final boolean enable) { remoteVerificationEnabled = enable; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/bsd/RLoginClient.java000066400000000000000000000125371434047722200316650ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.bsd; import java.io.IOException; /** * RLoginClient is very similar to {@link org.apache.commons.net.bsd.RCommandClient}, from which it is derived, and uses the rcmd() facility implemented in * RCommandClient to implement the functionality of the rlogin command that first appeared in 4.2BSD Unix. rlogin is a command used to login to a remote machine * from a trusted host, sometimes without issuing a password. The trust relationship is the same as described in the documentation for * {@link org.apache.commons.net.bsd.RCommandClient}. *

* As with virtually all of the client classes in org.apache.commons.net, this class derives from SocketClient. But it relies on the connection methods defined * in RcommandClient which ensure that the local Socket will originate from an acceptable rshell port. The way to use RLoginClient is to first connect to the * server, call the {@link #rlogin rlogin() } method, and then fetch the connection's input and output streams. Interaction with the remote command is * controlled entirely through the I/O streams. Once you have finished processing the streams, you should invoke * {@link org.apache.commons.net.bsd.RExecClient#disconnect disconnect() } to clean up properly. *

* The standard output and standard error streams of the remote process are transmitted over the same connection, readable from the input stream returned by * {@link org.apache.commons.net.bsd.RExecClient#getInputStream getInputStream() } *

* Unlike RExecClient and RCommandClient, it is not possible to tell the rlogind daemon to return the standard error stream over a separate connection. * {@link org.apache.commons.net.bsd.RExecClient#getErrorStream getErrorStream() } will always return null. The standard input of the remote process can be * written to through the output stream returned by {@link org.apache.commons.net.bsd.RExecClient#getOutputStream getOutputSream() } * * @see org.apache.commons.net.SocketClient * @see RExecClient * @see RCommandClient */ public class RLoginClient extends RCommandClient { /** * The default rlogin port. Set to 513 in BSD Unix and according to RFC 1282. */ public static final int DEFAULT_PORT = 513; /** * The default RLoginClient constructor. Initializes the default port to DEFAULT_PORT . */ public RLoginClient() { setDefaultPort(DEFAULT_PORT); } /** * Same as the other rlogin method, but no terminal speed is defined. * * @param localUsername the local user * @param remoteUsername the remote user * @param terminalType the terminal type * @throws IOException on error */ public void rlogin(final String localUsername, final String remoteUsername, final String terminalType) throws IOException { rexec(localUsername, remoteUsername, terminalType, false); } /** * Logins into a remote machine through the rlogind daemon on the server to which the RLoginClient is connected. After calling this method, you may interact * with the remote login shell through its standard input and output streams. Standard error is sent over the same stream as standard output. You will * typically be able to detect the termination of the remote login shell after reaching end of file on its standard output (accessible through * {@link #getInputStream getInputStream() }. Disconnecting from the server or closing the process streams before reaching end of file will terminate the * remote login shell in most cases. *

* If user authentication fails, the rlogind daemon will request that a password be entered interactively. You will be able to read the prompt from the * output stream of the RLoginClient and write the password to the input stream of the RLoginClient. * * @param localUsername The user account on the local machine that is trying to login to the remote host. * @param remoteUsername The account name on the server that is being logged in to. * @param terminalType The name of the user's terminal (e.g., "vt100", "network", etc.) * @param terminalSpeed The speed of the user's terminal, expressed as a baud rate or bps (e.g., 9600 or 38400) * @throws IOException If the rlogin() attempt fails. The exception will contain a message indicating the nature of the failure. */ public void rlogin(final String localUsername, final String remoteUsername, final String terminalType, final int terminalSpeed) throws IOException { rexec(localUsername, remoteUsername, terminalType + "/" + terminalSpeed, false); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/bsd/package-info.java000066400000000000000000000015661434047722200316600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Classes for rcommand, rexec, rlogin */ package org.apache.commons.net.bsd;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/chargen/000077500000000000000000000000001434047722200273205ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/chargen/CharGenTCPClient.java000066400000000000000000000060531434047722200332040ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.chargen; import java.io.InputStream; import org.apache.commons.net.SocketClient; /** * The CharGenTCPClient class is a TCP implementation of a client for the character generator protocol described in RFC 864. It can also be used for Systat (RFC * 866), Quote of the Day (RFC 865), and netstat (port 15). All of these protocols involve connecting to the appropriate port, and reading data from an input * stream. The chargen protocol actually sends data until the receiving end closes the connection. All of the others send only a fixed amount of data and then * close the connection. *

* To use the CharGenTCPClient class, just establish a connection with {@link org.apache.commons.net.SocketClient#connect connect } and call * {@link #getInputStream getInputStream() } to access the data. Don't close the input stream when you're done with it. Rather, call * {@link org.apache.commons.net.SocketClient#disconnect disconnect } to clean up properly. * * @see CharGenUDPClient */ public final class CharGenTCPClient extends SocketClient { /** The systat port value of 11 according to RFC 866. */ public static final int SYSTAT_PORT = 11; /** The netstat port value of 19. */ public static final int NETSTAT_PORT = 15; /** The quote of the day port value of 17 according to RFC 865. */ public static final int QUOTE_OF_DAY_PORT = 17; /** The character generator port value of 19 according to RFC 864. */ public static final int CHARGEN_PORT = 19; /** The default chargen port. It is set to 19 according to RFC 864. */ public static final int DEFAULT_PORT = 19; /** * The default constructor for CharGenTCPClient. It merely sets the default port to DEFAULT_PORT . */ public CharGenTCPClient() { setDefaultPort(DEFAULT_PORT); } /** * Returns an InputStream from which the server generated data can be read. You should NOT close the InputStream when you're finished reading from it. * Rather, you should call {@link org.apache.commons.net.SocketClient#disconnect disconnect } to clean up properly. * * @return An InputStream from which the server generated data can be read. */ public InputStream getInputStream() { return _input_; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/chargen/CharGenUDPClient.java000066400000000000000000000117131434047722200332050ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.chargen; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import org.apache.commons.net.DatagramSocketClient; import org.apache.commons.net.util.NetConstants; /** * The CharGenUDPClient class is a UDP implementation of a client for the character generator protocol described in RFC 864. It can also be used for Systat (RFC * 866), Quote of the Day (RFC 865), and netstat (port 15). All of these protocols involve sending a datagram to the appropriate port, and reading data * contained in one or more reply datagrams. The chargen and quote of the day protocols only send one reply datagram containing 512 bytes or less of data. The * other protocols may reply with more than one datagram, in which case you must wait for a timeout to determine that all reply datagrams have been sent. *

* To use the CharGenUDPClient class, just open a local UDP port with {@link org.apache.commons.net.DatagramSocketClient#open open } and call {@link #send send * } to send the datagram that will initiate the data reply. For chargen or quote of the day, just call {@link #receive receive }, and you're done. For netstat * and systat, call receive in a while loop, and catch a SocketException and InterruptedIOException to detect a timeout (don't forget to set the timeout * duration beforehand). Don't forget to call {@link org.apache.commons.net.DatagramSocketClient#close close() } to clean up properly. * * @see CharGenTCPClient */ public final class CharGenUDPClient extends DatagramSocketClient { /** The systat port value of 11 according to RFC 866. */ public static final int SYSTAT_PORT = 11; /** The netstat port value of 19. */ public static final int NETSTAT_PORT = 15; /** The quote of the day port value of 17 according to RFC 865. */ public static final int QUOTE_OF_DAY_PORT = 17; /** The character generator port value of 19 according to RFC 864. */ public static final int CHARGEN_PORT = 19; /** The default chargen port. It is set to 19 according to RFC 864. */ public static final int DEFAULT_PORT = 19; private final byte[] receiveData; private final DatagramPacket receivePacket; private final DatagramPacket sendPacket; /** * The default CharGenUDPClient constructor. It initializes some internal data structures for sending and receiving the necessary datagrams for the chargen * and related protocols. */ public CharGenUDPClient() { // CharGen return packets have a maximum length of 512 receiveData = new byte[512]; receivePacket = new DatagramPacket(receiveData, receiveData.length); sendPacket = new DatagramPacket(NetConstants.EMPTY_BTYE_ARRAY, 0); } /** * Receive the reply data from the server. This will always be 512 bytes or less. Chargen and quote of the day only return one packet. Netstat and systat * require multiple calls to receive() with timeout detection. * * @return The reply data from the server. * @throws IOException If an error occurs while receiving the datagram. */ public byte[] receive() throws IOException { final int length; final byte[] result; _socket_.receive(receivePacket); result = new byte[length = receivePacket.getLength()]; System.arraycopy(receiveData, 0, result, 0, length); return result; } /** * Same as send(host, CharGenUDPClient.DEFAULT_PORT); * * @param host the destination host * @throws IOException on error */ public void send(final InetAddress host) throws IOException { send(host, DEFAULT_PORT); } /** * Sends the data initiation datagram. This data in the packet is ignored by the server, and merely serves to signal that the server should send its reply. * * @param host The address of the server. * @param port The port of the service. * @throws IOException If an error occurs while sending the datagram. */ public void send(final InetAddress host, final int port) throws IOException { sendPacket.setAddress(host); sendPacket.setPort(port); _socket_.send(sendPacket); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/chargen/package-info.java000066400000000000000000000015571434047722200325170ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Chargen over TCP and UDP */ package org.apache.commons.net.chargen;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/daytime/000077500000000000000000000000001434047722200273455ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/daytime/DaytimeTCPClient.java000066400000000000000000000060131434047722200333120ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.daytime; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.apache.commons.net.SocketClient; /** * The DaytimeTCPClient class is a TCP implementation of a client for the Daytime protocol described in RFC 867. To use the class, merely establish a connection * with {@link org.apache.commons.net.SocketClient#connect connect } and call {@link #getTime getTime() } to retrieve the daytime string, then call * {@link org.apache.commons.net.SocketClient#disconnect disconnect } to close the connection properly. * * @see DaytimeUDPClient */ public final class DaytimeTCPClient extends SocketClient { /** The default daytime port. It is set to 13 according to RFC 867. */ public static final int DEFAULT_PORT = 13; // Received dates will likely be less than 64 characters. // This is a temporary buffer used while receiving data. private final char[] buffer = new char[64]; /** * The default DaytimeTCPClient constructor. It merely sets the default port to DEFAULT_PORT . */ public DaytimeTCPClient() { setDefaultPort(DEFAULT_PORT); } /** * Retrieves the time string from the server and returns it. The server will have closed the connection at this point, so you should call * {@link org.apache.commons.net.SocketClient#disconnect disconnect } after calling this method. To retrieve another time, you must initiate another * connection with {@link org.apache.commons.net.SocketClient#connect connect } before calling getTime() again. * * @return The time string retrieved from the server. * @throws IOException If an error occurs while fetching the time string. */ public String getTime() throws IOException { int read; final StringBuilder result = new StringBuilder(buffer.length); final BufferedReader reader; reader = new BufferedReader(new InputStreamReader(_input_, getCharset())); while (true) { read = reader.read(buffer, 0, buffer.length); if (read <= 0) { break; } result.append(buffer, 0, read); } return result.toString(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/daytime/DaytimeUDPClient.java000066400000000000000000000061701434047722200333200ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.daytime; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import org.apache.commons.net.DatagramSocketClient; /** * The DaytimeUDPClient class is a UDP implementation of a client for the Daytime protocol described in RFC 867. To use the class, merely open a local datagram * socket with {@link org.apache.commons.net.DatagramSocketClient#open open } and call {@link #getTime getTime } to retrieve the daytime string, then call * {@link org.apache.commons.net.DatagramSocketClient#close close } to close the connection properly. Unlike * {@link org.apache.commons.net.daytime.DaytimeTCPClient}, successive calls to {@link #getTime getTime } are permitted without re-establishing a connection. * That is because UDP is a connectionless protocol and the Daytime protocol is stateless. * * @see DaytimeTCPClient */ public final class DaytimeUDPClient extends DatagramSocketClient { /** The default daytime port. It is set to 13 according to RFC 867. */ public static final int DEFAULT_PORT = 13; private final byte[] dummyData = new byte[1]; // Received dates should be less than 256 bytes private final byte[] timeData = new byte[256]; /** * Same as getTime(host, DaytimeUDPClient.DEFAULT_PORT); * * @param host the host * @return the time * @throws IOException on error */ public String getTime(final InetAddress host) throws IOException { return getTime(host, DEFAULT_PORT); } /** * Retrieves the time string from the specified server and port and returns it. * * @param host The address of the server. * @param port The port of the service. * @return The time string. * @throws IOException If an error occurs while retrieving the time. */ public String getTime(final InetAddress host, final int port) throws IOException { final DatagramPacket sendPacket; final DatagramPacket receivePacket; sendPacket = new DatagramPacket(dummyData, dummyData.length, host, port); receivePacket = new DatagramPacket(timeData, timeData.length); _socket_.send(sendPacket); _socket_.receive(receivePacket); return new String(receivePacket.getData(), 0, receivePacket.getLength(), getCharset()); // Java 1.6 can use getCharset() } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/daytime/package-info.java000066400000000000000000000015571434047722200325440ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Daytime over TCP and UDP */ package org.apache.commons.net.daytime;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/discard/000077500000000000000000000000001434047722200273225ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/discard/DiscardTCPClient.java000066400000000000000000000043531434047722200332510ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.discard; import java.io.OutputStream; import org.apache.commons.net.SocketClient; /** * The DiscardTCPClient class is a TCP implementation of a client for the Discard protocol described in RFC 863. To use the class, merely establish a connection * with {@link org.apache.commons.net.SocketClient#connect connect } and call {@link #getOutputStream getOutputStream() } to retrieve the discard output stream. * Don't close the output stream when you're done writing to it. Rather, call {@link org.apache.commons.net.SocketClient#disconnect disconnect } to clean up * properly. * * @see DiscardUDPClient */ public class DiscardTCPClient extends SocketClient { /** The default discard port. It is set to 9 according to RFC 863. */ public static final int DEFAULT_PORT = 9; /** * The default DiscardTCPClient constructor. It merely sets the default port to DEFAULT_PORT . */ public DiscardTCPClient() { setDefaultPort(DEFAULT_PORT); } /** * Returns an OutputStream through which you may write data to the server. You should NOT close the OutputStream when you're finished reading from it. * Rather, you should call {@link org.apache.commons.net.SocketClient#disconnect disconnect } to clean up properly. * * @return An OutputStream through which you can write data to the server. */ public OutputStream getOutputStream() { return _output_; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/discard/DiscardUDPClient.java000066400000000000000000000066651434047722200332630ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.discard; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import org.apache.commons.net.DatagramSocketClient; import org.apache.commons.net.util.NetConstants; /** * The DiscardUDPClient class is a UDP implementation of a client for the Discard protocol described in RFC 863. To use the class, just open a local UDP port * with {@link org.apache.commons.net.DatagramSocketClient#open open } and call {@link #send send } to send datagrams to the server After you're done sending * discard data, call {@link org.apache.commons.net.DatagramSocketClient#close close() } to clean up properly. * * @see DiscardTCPClient */ public class DiscardUDPClient extends DatagramSocketClient { /** The default discard port. It is set to 9 according to RFC 863. */ public static final int DEFAULT_PORT = 9; private final DatagramPacket sendPacket; public DiscardUDPClient() { sendPacket = new DatagramPacket(NetConstants.EMPTY_BTYE_ARRAY, 0); } /** * Same as send(data, data.length, host. DiscardUDPClient.DEFAULT_PORT). * * @param data the buffer to send * @param host the target host * @see #send(byte[], int, InetAddress, int) * @throws IOException if an error occurs */ public void send(final byte[] data, final InetAddress host) throws IOException { send(data, data.length, host, DEFAULT_PORT); } /** * Same as send(data, length, host. DiscardUDPClient.DEFAULT_PORT). * * @param data the buffer to send * @param length the length of the data in the buffer * @param host the target host * @see #send(byte[], int, InetAddress, int) * @throws IOException if an error occurs */ public void send(final byte[] data, final int length, final InetAddress host) throws IOException { send(data, length, host, DEFAULT_PORT); } /** * Sends the specified data to the specified server at the specified port. * * @param data The discard data to send. * @param length The length of the data to send. Should be less than or equal to the length of the data byte array. * @param host The address of the server. * @param port The service port. * @throws IOException If an error occurs during the datagram send operation. */ public void send(final byte[] data, final int length, final InetAddress host, final int port) throws IOException { sendPacket.setData(data); sendPacket.setLength(length); sendPacket.setAddress(host); sendPacket.setPort(port); _socket_.send(sendPacket); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/discard/package-info.java000066400000000000000000000015571434047722200325210ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Discard over TCP and UDP */ package org.apache.commons.net.discard;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/echo/000077500000000000000000000000001434047722200266275ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/echo/EchoTCPClient.java000066400000000000000000000045271434047722200320660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.echo; import java.io.InputStream; import org.apache.commons.net.discard.DiscardTCPClient; /** * The EchoTCPClient class is a TCP implementation of a client for the Echo protocol described in RFC 862. To use the class, merely establish a connection with * {@link org.apache.commons.net.SocketClient#connect connect } and call {@link DiscardTCPClient#getOutputStream getOutputStream() } to retrieve the echo output * stream and {@link #getInputStream getInputStream() } to get the echo input stream. Don't close either stream when you're done using them. Rather, call * {@link org.apache.commons.net.SocketClient#disconnect disconnect } to clean up properly. * * @see EchoUDPClient * @see DiscardTCPClient */ public final class EchoTCPClient extends DiscardTCPClient { /** The default echo port. It is set to 7 according to RFC 862. */ public static final int DEFAULT_PORT = 7; /** * The default EchoTCPClient constructor. It merely sets the default port to DEFAULT_PORT . */ public EchoTCPClient() { setDefaultPort(DEFAULT_PORT); } /** * Returns an InputStream from which you may read echoed data from the server. You should NOT close the InputStream when you're finished reading from it. * Rather, you should call {@link org.apache.commons.net.SocketClient#disconnect disconnect } to clean up properly. * * @return An InputStream from which you can read echoed data from the server. */ public InputStream getInputStream() { return _input_; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/echo/EchoUDPClient.java000066400000000000000000000071351434047722200320660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.echo; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import org.apache.commons.net.discard.DiscardUDPClient; import org.apache.commons.net.util.NetConstants; /** * The EchoUDPClient class is a UDP implementation of a client for the Echo protocol described in RFC 862. To use the class, just open a local UDP port with * {@link org.apache.commons.net.DatagramSocketClient#open open } and call {@link #send send } to send datagrams to the server, then call {@link #receive * receive } to receive echoes. After you're done echoing data, call {@link org.apache.commons.net.DatagramSocketClient#close close() } to clean up properly. * * @see EchoTCPClient * @see DiscardUDPClient */ public final class EchoUDPClient extends DiscardUDPClient { /** The default echo port. It is set to 7 according to RFC 862. */ public static final int DEFAULT_PORT = 7; private final DatagramPacket receivePacket = new DatagramPacket(NetConstants.EMPTY_BTYE_ARRAY, 0); /** * Same as receive(data, data.length) * * @param data the buffer to receive the input * @return the number of bytes * @throws IOException on error */ public int receive(final byte[] data) throws IOException { return receive(data, data.length); } /** * Receives echoed data and returns its length. The data may be divided up among multiple datagrams, requiring multiple calls to receive. Also, the UDP * packets will not necessarily arrive in the same order they were sent. * * @param data the buffer to receive the input * @param length of the buffer * * @return Length of actual data received. * @throws IOException If an error occurs while receiving the data. */ public int receive(final byte[] data, final int length) throws IOException { receivePacket.setData(data); receivePacket.setLength(length); _socket_.receive(receivePacket); return receivePacket.getLength(); } /** Same as send(data, data.length, host) */ @Override public void send(final byte[] data, final InetAddress host) throws IOException { send(data, data.length, host, DEFAULT_PORT); } /** * Sends the specified data to the specified server at the default echo port. * * @param data The echo data to send. * @param length The length of the data to send. Should be less than or equal to the length of the data byte array. * @param host The address of the server. * @throws IOException If an error occurs during the datagram send operation. */ @Override public void send(final byte[] data, final int length, final InetAddress host) throws IOException { send(data, length, host, DEFAULT_PORT); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/echo/package-info.java000066400000000000000000000015511434047722200320200ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Echo over TCP and UDP */ package org.apache.commons.net.echo;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/000077500000000000000000000000001434047722200275275ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/Main.java000066400000000000000000000105241434047722200312600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.examples; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.CodeSource; import java.util.Collections; import java.util.List; import java.util.Properties; /** * Helper application for example classes. */ public class Main { private static boolean fromJar() { final CodeSource codeSource = Main.class.getProtectionDomain().getCodeSource(); if (codeSource != null) { return codeSource.getLocation().getFile().endsWith(".jar"); } return false; // No idea if this can happen } /** * Helper application for example classes. Lists available classes, and provides shorthand invocation. For example:
* java -jar commons-net-examples-m.n.jar FTPClientExample -l host user password * * @param args the first argument is used to name the class; remaining arguments are passed to the target class. * @throws Throwable if an error occurs */ public static void main(final String[] args) throws Throwable { final Properties fp = new Properties(); final InputStream ras = Main.class.getResourceAsStream("examples.properties"); if (ras != null) { fp.load(ras); } else { System.err.println("[Cannot find examples.properties file, so aliases cannot be used]"); } if (args.length == 0) { if (Thread.currentThread().getStackTrace().length > 2) { // called by Maven System.out.println( "Usage: mvn -q exec:java -Dexec.arguments=, (comma-separated, no spaces)"); System.out.println("Or : mvn -q exec:java -Dexec.args=\" \" (space separated)"); } else if (fromJar()) { System.out.println("Usage: java -jar commons-net-examples-m.n.jar "); } else { System.out .println("Usage: java -cp target/classes org.apache.commons.net.examples.Main" + " "); } @SuppressWarnings("unchecked") // property names are Strings final List l = (List) Collections.list(fp.propertyNames()); if (l.isEmpty()) { return; } l.sort(null); System.out.println("\nAliases and their classes:"); for (final String s : l) { System.out.printf("%-25s %s%n", s, fp.getProperty(s)); } return; } final String shortName = args[0]; String fullName = fp.getProperty(shortName); if (fullName == null) { fullName = shortName; } try { final Class clazz = Class.forName(fullName); final Method m = clazz.getDeclaredMethod("main", args.getClass()); final String[] args2 = new String[args.length - 1]; System.arraycopy(args, 1, args2, 0, args2.length); try { m.invoke(null, (Object) args2); } catch (final InvocationTargetException ite) { final Throwable cause = ite.getCause(); if (cause != null) { throw cause; } throw ite; } } catch (final ClassNotFoundException e) { System.out.println(e); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/cidr/000077500000000000000000000000001434047722200304505ustar00rootroot00000000000000SubnetUtilsExample.java000066400000000000000000000062201434047722200350310ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/cidr/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.cidr; import java.util.Arrays; import java.util.Scanner; import org.apache.commons.net.util.SubnetUtils; import org.apache.commons.net.util.SubnetUtils.SubnetInfo; /** * Example class that shows how to use the {@link SubnetUtils} class. * */ public class SubnetUtilsExample { public static void main(final String[] args) { final String subnet = "192.168.0.3/31"; final SubnetUtils utils = new SubnetUtils(subnet); final SubnetInfo info = utils.getInfo(); System.out.printf("Subnet Information for %s:%n", subnet); System.out.println("--------------------------------------"); System.out.printf("IP Address:\t\t\t%s\t[%s]%n", info.getAddress(), Integer.toBinaryString(info.asInteger(info.getAddress()))); System.out.printf("Netmask:\t\t\t%s\t[%s]%n", info.getNetmask(), Integer.toBinaryString(info.asInteger(info.getNetmask()))); System.out.printf("CIDR Representation:\t\t%s%n%n", info.getCidrSignature()); System.out.printf("Supplied IP Address:\t\t%s%n%n", info.getAddress()); System.out.printf("Network Address:\t\t%s\t[%s]%n", info.getNetworkAddress(), Integer.toBinaryString(info.asInteger(info.getNetworkAddress()))); System.out.printf("Broadcast Address:\t\t%s\t[%s]%n", info.getBroadcastAddress(), Integer.toBinaryString(info.asInteger(info.getBroadcastAddress()))); System.out.printf("Low Address:\t\t\t%s\t[%s]%n", info.getLowAddress(), Integer.toBinaryString(info.asInteger(info.getLowAddress()))); System.out.printf("High Address:\t\t\t%s\t[%s]%n", info.getHighAddress(), Integer.toBinaryString(info.asInteger(info.getHighAddress()))); System.out.printf("Total usable addresses: \t%d%n", Long.valueOf(info.getAddressCountLong())); System.out.printf("Address List: %s%n%n", Arrays.toString(info.getAllAddresses())); final String prompt = "Enter an IP address (e.g. 192.168.0.10):"; System.out.println(prompt); try (final Scanner scanner = new Scanner(System.in)) { while (scanner.hasNextLine()) { final String address = scanner.nextLine(); System.out.println("The IP address [" + address + "] is " + (info.isInRange(address) ? "" : "not ") + "within the subnet [" + subnet + "]"); System.out.println(prompt); } } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ftp/000077500000000000000000000000001434047722200303205ustar00rootroot00000000000000FTPClientExample.java000066400000000000000000000512371434047722200342200ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.ftp; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.InetAddress; import java.net.UnknownHostException; import java.time.Duration; import java.util.Arrays; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPConnectionClosedException; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPHTTPClient; import org.apache.commons.net.ftp.FTPReply; import org.apache.commons.net.ftp.FTPSClient; import org.apache.commons.net.io.CopyStreamEvent; import org.apache.commons.net.io.CopyStreamListener; import org.apache.commons.net.util.TrustManagerUtils; /** * This is an example program demonstrating how to use the FTPClient class. This program connects to an FTP server and retrieves the specified file. If the -s * flag is used, it stores the local file at the FTP server. Just so you can see what's happening, all reply strings are printed. If the -b flag is used, a * binary transfer is assumed (default is ASCII). See below for further options. */ public final class FTPClientExample { public static final String USAGE = "Expected Parameters: [options] [ []]\n" + "\nDefault behavior is to download a file and use ASCII transfer mode.\n" + "\t-a - use local active mode (default is local passive)\n" + "\t-A - anonymous login (omit username and password parameters)\n" + "\t-b - use binary transfer mode\n" + "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n" + "\t-d - list directory details using MLSD (remote is used as the pathname if provided)\n" + "\t-e - use EPSV with IPv4 (default false)\n" + "\t-E - encoding to use for control channel\n" + "\t-f - issue FEAT command (remote and local files are ignored)\n" + "\t-h - list hidden files (applies to -l and -n only)\n" + "\t-i - issue SIZE command for a file\n" + "\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n" + "\t-l - list files using LIST (remote is used as the pathname if provided)\n" + "\t Files are listed twice: first in raw mode, then as the formatted parsed data.\n" + "\t N.B. if the wrong server-type is used, output may be lost. Use -U or -S as necessary.\n" + "\t-L - use lenient future dates (server dates may be up to 1 day into future)\n" + "\t-m - list file details using MDTM (remote is used as the pathname if provided)\n" + "\t-n - list file names using NLST (remote is used as the pathname if provided)\n" + "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n" + "\t-s - store file on server (upload)\n" + "\t-S - systemType set server system type (e.g. UNIX VMS WINDOWS)\n" + "\t-t - list file details using MLST (remote is used as the pathname if provided)\n" + "\t-U - save unparseable responses\n" + "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n" + "\t-T all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n" + "\t-y format - set default date format string\n" + "\t-Y format - set recent date format string\n" + "\t-Z timezone - set the server time zone for parsing LIST responses\n" + "\t-z timezone - set the time zone for displaying MDTM, LIST, MLSD, MLST responses\n" + "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n" + "\t-PrU user - HTTP Proxy server username\n" + "\t-PrP password - HTTP Proxy server password\n" + "\t-# - add hash display during transfers\n"; private static CopyStreamListener createListener() { return new CopyStreamListener() { private long megsTotal; @Override public void bytesTransferred(final CopyStreamEvent event) { bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize()); } @Override public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { final long megs = totalBytesTransferred / 1000000; for (long l = megsTotal; l < megs; l++) { System.err.print("#"); } megsTotal = megs; } }; } public static void main(final String[] args) throws UnknownHostException { boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false; boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false; boolean mlst = false, mlsd = false, mdtm = false, saveUnparseable = false; boolean size = false; boolean lenient = false; long keepAliveTimeoutSeconds = -1; int controlKeepAliveReplyTimeoutMillis = -1; int minParams = 5; // listings require 3 params String protocol = null; // SSL protocol String doCommand = null; String trustmgr = null; String proxyHost = null; int proxyPort = 80; String proxyUser = null; String proxyPassword = null; String username = null; String password = null; String encoding = null; String serverTimeZoneId = null; String displayTimeZoneId = null; String serverType = null; String defaultDateFormat = null; String recentDateFormat = null; int base = 0; for (base = 0; base < args.length; base++) { if (args[base].equals("-s")) { storeFile = true; } else if (args[base].equals("-a")) { localActive = true; } else if (args[base].equals("-A")) { username = "anonymous"; password = System.getProperty("user.name") + "@" + InetAddress.getLocalHost().getHostName(); } else if (args[base].equals("-b")) { binaryTransfer = true; } else if (args[base].equals("-c")) { doCommand = args[++base]; minParams = 3; } else if (args[base].equals("-d")) { mlsd = true; minParams = 3; } else if (args[base].equals("-e")) { useEpsvWithIPv4 = true; } else if (args[base].equals("-E")) { encoding = args[++base]; } else if (args[base].equals("-f")) { feat = true; minParams = 3; } else if (args[base].equals("-h")) { hidden = true; } else if (args[base].equals("-i")) { size = true; minParams = 3; } else if (args[base].equals("-k")) { keepAliveTimeoutSeconds = Long.parseLong(args[++base]); } else if (args[base].equals("-l")) { listFiles = true; minParams = 3; } else if (args[base].equals("-m")) { mdtm = true; minParams = 3; } else if (args[base].equals("-L")) { lenient = true; } else if (args[base].equals("-n")) { listNames = true; minParams = 3; } else if (args[base].equals("-p")) { protocol = args[++base]; } else if (args[base].equals("-S")) { serverType = args[++base]; } else if (args[base].equals("-t")) { mlst = true; minParams = 3; } else if (args[base].equals("-U")) { saveUnparseable = true; } else if (args[base].equals("-w")) { controlKeepAliveReplyTimeoutMillis = Integer.parseInt(args[++base]); } else if (args[base].equals("-T")) { trustmgr = args[++base]; } else if (args[base].equals("-y")) { defaultDateFormat = args[++base]; } else if (args[base].equals("-Y")) { recentDateFormat = args[++base]; } else if (args[base].equals("-Z")) { serverTimeZoneId = args[++base]; } else if (args[base].equals("-z")) { displayTimeZoneId = args[++base]; } else if (args[base].equals("-PrH")) { proxyHost = args[++base]; final String parts[] = proxyHost.split(":"); if (parts.length == 2) { proxyHost = parts[0]; proxyPort = Integer.parseInt(parts[1]); } } else if (args[base].equals("-PrU")) { proxyUser = args[++base]; } else if (args[base].equals("-PrP")) { proxyPassword = args[++base]; } else if (args[base].equals("-#")) { printHash = true; } else { break; } } final int remain = args.length - base; if (username != null) { minParams -= 2; } if (remain < minParams) // server, user, pass, remote, local [protocol] { if (args.length > 0) { System.err.println("Actual Parameters: " + Arrays.toString(args)); } System.err.println(USAGE); System.exit(1); } String server = args[base++]; int port = 0; final String parts[] = server.split(":"); if (parts.length == 2) { server = parts[0]; port = Integer.parseInt(parts[1]); } if (username == null) { username = args[base++]; password = args[base++]; } String remote = null; if (args.length - base > 0) { remote = args[base++]; } String local = null; if (args.length - base > 0) { local = args[base++]; } final FTPClient ftp; if (protocol == null) { if (proxyHost != null) { System.out.println("Using HTTP proxy server: " + proxyHost); ftp = new FTPHTTPClient(proxyHost, proxyPort, proxyUser, proxyPassword); } else { ftp = new FTPClient(); } } else { final FTPSClient ftps; if (protocol.equals("true")) { ftps = new FTPSClient(true); } else if (protocol.equals("false")) { ftps = new FTPSClient(false); } else { final String prot[] = protocol.split(","); if (prot.length == 1) { // Just protocol ftps = new FTPSClient(protocol); } else { // protocol,true|false ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1])); } } ftp = ftps; if ("all".equals(trustmgr)) { ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager()); } else if ("valid".equals(trustmgr)) { ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager()); } else if ("none".equals(trustmgr)) { ftps.setTrustManager(null); } } if (printHash) { ftp.setCopyStreamListener(createListener()); } if (keepAliveTimeoutSeconds >= 0) { ftp.setControlKeepAliveTimeout(Duration.ofSeconds(keepAliveTimeoutSeconds)); } if (controlKeepAliveReplyTimeoutMillis >= 0) { ftp.setControlKeepAliveReplyTimeout(Duration.ofMillis(controlKeepAliveReplyTimeoutMillis)); } if (encoding != null) { ftp.setControlEncoding(encoding); } ftp.setListHiddenFiles(hidden); // suppress login details ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); final FTPClientConfig config; if (serverType != null) { config = new FTPClientConfig(serverType); } else { config = new FTPClientConfig(); } config.setUnparseableEntries(saveUnparseable); if (defaultDateFormat != null) { config.setDefaultDateFormatStr(defaultDateFormat); } if (recentDateFormat != null) { config.setRecentDateFormatStr(recentDateFormat); } ftp.configure(config); try { final int reply; if (port > 0) { ftp.connect(server, port); } else { ftp.connect(server); } System.out.println("Connected to " + server + " on " + (port > 0 ? port : ftp.getDefaultPort())); // After connection attempt, you should check the reply code to verify // success. reply = ftp.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp.disconnect(); System.err.println("FTP server refused connection."); System.exit(1); } } catch (final IOException e) { if (ftp.isConnected()) { try { ftp.disconnect(); } catch (final IOException f) { // do nothing } } System.err.println("Could not connect to server."); e.printStackTrace(); System.exit(1); } __main: try { if (!ftp.login(username, password)) { ftp.logout(); error = true; break __main; } System.out.println("Remote system is " + ftp.getSystemType()); if (binaryTransfer) { ftp.setFileType(FTP.BINARY_FILE_TYPE); } else { // in theory this should not be necessary as servers should default to ASCII // but they don't all do so - see NET-500 ftp.setFileType(FTP.ASCII_FILE_TYPE); } // Use passive mode as default because most of us are // behind firewalls these days. if (localActive) { ftp.enterLocalActiveMode(); } else { ftp.enterLocalPassiveMode(); } ftp.setUseEPSVwithIPv4(useEpsvWithIPv4); if (storeFile) { try (final InputStream input = new FileInputStream(local)) { ftp.storeFile(remote, input); } if (keepAliveTimeoutSeconds > 0) { showCslStats(ftp); } } // Allow multiple list types for single invocation else if (listFiles || mlsd || mdtm || mlst || listNames || size) { if (mlsd) { for (final FTPFile f : ftp.mlistDir(remote)) { System.out.println(f.getRawListing()); System.out.println(f.toFormattedString(displayTimeZoneId)); } } if (mdtm) { final FTPFile f = ftp.mdtmFile(remote); if (f != null) { System.out.println(f.getRawListing()); System.out.println(f.toFormattedString(displayTimeZoneId)); } else { System.out.println("File not found"); } } if (mlst) { final FTPFile f = ftp.mlistFile(remote); if (f != null) { System.out.println(f.toFormattedString(displayTimeZoneId)); } } if (listNames) { for (final String s : ftp.listNames(remote)) { System.out.println(s); } } if (size) { System.out.println("Size=" + ftp.getSize(remote)); } // Do this last because it changes the client if (listFiles) { if (lenient || serverTimeZoneId != null) { config.setLenientFutureDates(lenient); if (serverTimeZoneId != null) { config.setServerTimeZoneId(serverTimeZoneId); } ftp.configure(config); } for (final FTPFile f : ftp.listFiles(remote)) { System.out.println(f.getRawListing()); System.out.println(f.toFormattedString(displayTimeZoneId)); } } } else if (feat) { // boolean feature check if (remote != null) { // See if the command is present if (ftp.hasFeature(remote)) { System.out.println("Has feature: " + remote); } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { System.out.println("FEAT " + remote + " was not detected"); } else { System.out.println("Command failed: " + ftp.getReplyString()); } // Strings feature check final String[] features = ftp.featureValues(remote); if (features != null) { for (final String f : features) { System.out.println("FEAT " + remote + "=" + f + "."); } } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { System.out.println("FEAT " + remote + " is not present"); } else { System.out.println("Command failed: " + ftp.getReplyString()); } } else if (ftp.features()) { // Command listener has already printed the output } else { System.out.println("Failed: " + ftp.getReplyString()); } } else if (doCommand != null) { if (ftp.doCommand(doCommand, remote)) { // Command listener has already printed the output // for(String s : ftp.getReplyStrings()) { // System.out.println(s); // } } else { System.out.println("Failed: " + ftp.getReplyString()); } } else { try (final OutputStream output = new FileOutputStream(local)) { ftp.retrieveFile(remote, output); } if (keepAliveTimeoutSeconds > 0) { showCslStats(ftp); } } ftp.noop(); // check that control connection is working OK ftp.logout(); } catch (final FTPConnectionClosedException e) { error = true; System.err.println("Server closed connection."); e.printStackTrace(); } catch (final IOException e) { error = true; e.printStackTrace(); } finally { if (ftp.isConnected()) { try { ftp.disconnect(); } catch (final IOException f) { // do nothing } } } System.exit(error ? 1 : 0); } // end main private static void showCslStats(final FTPClient ftp) { @SuppressWarnings("deprecation") // debug code final int[] stats = ftp.getCslDebug(); System.out.println("CslDebug=" + Arrays.toString(stats)); } } ServerToServerFTP.java000066400000000000000000000155441434047722200344270ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.ftp; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.ProtocolCommandListener; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; /** * This is an example program demonstrating how to use the FTPClient class. This program arranges a server to server file transfer that transfers a file from * host1 to host2. Keep in mind, this program might only work if host2 is the same as the host you run it on (for security reasons, some ftp servers only allow * PORT commands to be issued with a host argument equal to the client host). *

* Usage: ftp */ public final class ServerToServerFTP { public static void main(final String[] args) { String server1; final String username1; final String password1; final String file1; String server2; final String username2; final String password2; final String file2; String[] parts; int port1 = 0, port2 = 0; final FTPClient ftp1; final FTPClient ftp2; final ProtocolCommandListener listener; if (args.length < 8) { System.err.println("Usage: ftp "); System.exit(1); } server1 = args[0]; parts = server1.split(":"); if (parts.length == 2) { server1 = parts[0]; port1 = Integer.parseInt(parts[1]); } username1 = args[1]; password1 = args[2]; file1 = args[3]; server2 = args[4]; parts = server2.split(":"); if (parts.length == 2) { server2 = parts[0]; port2 = Integer.parseInt(parts[1]); } username2 = args[5]; password2 = args[6]; file2 = args[7]; listener = new PrintCommandListener(new PrintWriter(System.out), true); ftp1 = new FTPClient(); ftp1.addProtocolCommandListener(listener); ftp2 = new FTPClient(); ftp2.addProtocolCommandListener(listener); try { final int reply; if (port1 > 0) { ftp1.connect(server1, port1); } else { ftp1.connect(server1); } System.out.println("Connected to " + server1 + "."); reply = ftp1.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp1.disconnect(); System.err.println("FTP server1 refused connection."); System.exit(1); } } catch (final IOException e) { if (ftp1.isConnected()) { try { ftp1.disconnect(); } catch (final IOException f) { // do nothing } } System.err.println("Could not connect to server1."); e.printStackTrace(); System.exit(1); } try { final int reply; if (port2 > 0) { ftp2.connect(server2, port2); } else { ftp2.connect(server2); } System.out.println("Connected to " + server2 + "."); reply = ftp2.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp2.disconnect(); System.err.println("FTP server2 refused connection."); System.exit(1); } } catch (final IOException e) { if (ftp2.isConnected()) { try { ftp2.disconnect(); } catch (final IOException f) { // do nothing } } System.err.println("Could not connect to server2."); e.printStackTrace(); System.exit(1); } __main: try { if (!ftp1.login(username1, password1)) { System.err.println("Could not login to " + server1); break __main; } if (!ftp2.login(username2, password2)) { System.err.println("Could not login to " + server2); break __main; } // Let's just assume success for now. ftp2.enterRemotePassiveMode(); ftp1.enterRemoteActiveMode(InetAddress.getByName(ftp2.getPassiveHost()), ftp2.getPassivePort()); // Although you would think the store command should be sent to server2 // first, in reality, ftp servers like wu-ftpd start accepting data // connections right after entering passive mode. Additionally, they // don't even send the positive preliminary reply until after the // transfer is completed (in the case of passive mode transfers). // Therefore, calling store first would hang waiting for a preliminary // reply. if (!ftp1.remoteRetrieve(file1) || !ftp2.remoteStoreUnique(file2)) { System.err.println("Couldn't initiate transfer. Check that file names are valid."); break __main; } // if(ftp1.remoteRetrieve(file1) && ftp2.remoteStore(file2)) { // We have to fetch the positive completion reply. ftp1.completePendingCommand(); ftp2.completePendingCommand(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } finally { try { if (ftp1.isConnected()) { ftp1.logout(); ftp1.disconnect(); } } catch (final IOException e) { // do nothing } try { if (ftp2.isConnected()) { ftp2.logout(); ftp2.disconnect(); } } catch (final IOException e) { // do nothing } } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ftp/TFTPExample.java000066400000000000000000000221041434047722200332530ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.ftp; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.SocketException; import java.net.UnknownHostException; import org.apache.commons.net.tftp.TFTP; import org.apache.commons.net.tftp.TFTPClient; import org.apache.commons.net.tftp.TFTPPacket; /** * This is an example of a simple Java tftp client. Notice how all of the code is really just argument processing and error handling. *

* Usage: tftp [options] hostname localfile remotefile hostname - The name of the remote host, with optional :port localfile - The name of the local file to * send or the name to use for the received file remotefile - The name of the remote file to receive or the name for the remote server to use to name the local * file being sent. options: (The default is to assume -r -b) -s Send a local file -r Receive a remote file -a Use ASCII transfer mode -b Use binary transfer * mode */ public final class TFTPExample { static final String USAGE = "Usage: tftp [options] hostname localfile remotefile\n\n" + "hostname - The name of the remote host [:port]\n" + "localfile - The name of the local file to send or the name to use for\n" + "\tthe received file\n" + "remotefile - The name of the remote file to receive or the name for\n" + "\tthe remote server to use to name the local file being sent.\n\n" + "options: (The default is to assume -r -b)\n" + "\t-t timeout in seconds (default 60s)\n" + "\t-s Send a local file\n" + "\t-r Receive a remote file\n" + "\t-a Use ASCII transfer mode\n" + "\t-b Use binary transfer mode\n" + "\t-v Verbose (trace packets)\n"; private static boolean close(final TFTPClient tftp, final Closeable output) { boolean closed; tftp.close(); try { if (output != null) { output.close(); } closed = true; } catch (final IOException e) { closed = false; System.err.println("Error: error closing file."); System.err.println(e.getMessage()); } return closed; } public static void main(final String[] args) { boolean receiveFile = true, closed; int transferMode = TFTP.BINARY_MODE, argc; String arg; final String hostname; final String localFilename; final String remoteFilename; final TFTPClient tftp; int timeout = 60000; boolean verbose = false; // Parse options for (argc = 0; argc < args.length; argc++) { arg = args[argc]; if (!arg.startsWith("-")) { break; } if (arg.equals("-r")) { receiveFile = true; } else if (arg.equals("-s")) { receiveFile = false; } else if (arg.equals("-a")) { transferMode = TFTP.ASCII_MODE; } else if (arg.equals("-b")) { transferMode = TFTP.BINARY_MODE; } else if (arg.equals("-t")) { timeout = 1000 * Integer.parseInt(args[++argc]); } else if (arg.equals("-v")) { verbose = true; } else { System.err.println("Error: unrecognized option."); System.err.print(USAGE); System.exit(1); } } // Make sure there are enough arguments if (args.length - argc != 3) { System.err.println("Error: invalid number of arguments."); System.err.print(USAGE); System.exit(1); } // Get host and file arguments hostname = args[argc]; localFilename = args[argc + 1]; remoteFilename = args[argc + 2]; // Create our TFTP instance to handle the file transfer. if (verbose) { tftp = new TFTPClient() { @Override protected void trace(final String direction, final TFTPPacket packet) { System.out.println(direction + " " + packet); } }; } else { tftp = new TFTPClient(); } // We want to timeout if a response takes longer than 60 seconds tftp.setDefaultTimeout(timeout); // We haven't closed the local file yet. closed = false; // If we're receiving a file, receive, otherwise send. if (receiveFile) { closed = receive(transferMode, hostname, localFilename, remoteFilename, tftp); } else { // We're sending a file closed = send(transferMode, hostname, localFilename, remoteFilename, tftp); } System.out.println("Recd: " + tftp.getTotalBytesReceived() + " Sent: " + tftp.getTotalBytesSent()); if (!closed) { System.out.println("Failed"); System.exit(1); } System.out.println("OK"); } private static void open(final TFTPClient tftp) { try { tftp.open(); } catch (final SocketException e) { throw new RuntimeException("Error: could not open local UDP socket.", e); } } private static boolean receive(final int transferMode, final String hostname, final String localFilename, final String remoteFilename, final TFTPClient tftp) { final boolean closed; FileOutputStream output = null; final File file; file = new File(localFilename); // If file exists, don't overwrite it. if (file.exists()) { System.err.println("Error: " + localFilename + " already exists."); return false; } // Try to open local file for writing try { output = new FileOutputStream(file); } catch (final IOException e) { tftp.close(); throw new RuntimeException("Error: could not open local file for writing.", e); } open(tftp); // Try to receive remote file via TFTP try { final String[] parts = hostname.split(":"); if (parts.length == 2) { tftp.receiveFile(remoteFilename, transferMode, output, parts[0], Integer.parseInt(parts[1])); } else { tftp.receiveFile(remoteFilename, transferMode, output, hostname); } } catch (final UnknownHostException e) { System.err.println("Error: could not resolve hostname."); System.err.println(e.getMessage()); System.exit(1); } catch (final IOException e) { System.err.println("Error: I/O exception occurred while receiving file."); System.err.println(e.getMessage()); System.exit(1); } finally { // Close local socket and output file closed = close(tftp, output); } return closed; } private static boolean send(final int transferMode, final String hostname, final String localFilename, final String remoteFilename, final TFTPClient tftp) { final boolean closed; FileInputStream input = null; // Try to open local file for reading try { input = new FileInputStream(localFilename); } catch (final IOException e) { tftp.close(); throw new RuntimeException("Error: could not open local file for reading.", e); } open(tftp); // Try to send local file via TFTP try { final String[] parts = hostname.split(":"); if (parts.length == 2) { tftp.sendFile(remoteFilename, transferMode, input, parts[0], Integer.parseInt(parts[1])); } else { tftp.sendFile(remoteFilename, transferMode, input, hostname); } } catch (final UnknownHostException e) { System.err.println("Error: could not resolve hostname."); System.err.println(e.getMessage()); System.exit(1); } catch (final IOException e) { System.err.println("Error: I/O exception occurred while sending file."); System.err.println(e.getMessage()); System.exit(1); } finally { // Close local socket and input file closed = close(tftp, input); } return closed; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/000077500000000000000000000000001434047722200304515ustar00rootroot00000000000000IMAPExportMbox.java000066400000000000000000000475731434047722200340330ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.ProtocolCommandEvent; import org.apache.commons.net.imap.IMAP; import org.apache.commons.net.imap.IMAP.IMAPChunkListener; import org.apache.commons.net.imap.IMAPClient; import org.apache.commons.net.imap.IMAPReply; /** * This is an example program demonstrating how to use the IMAP[S]Client class. This program connects to a IMAP[S] server and exports selected messages from a * folder into an mbox file. *

* Usage: IMAPExportMbox imap[s]://user:password@host[:port]/folder/path [sequence-set] [item-names] *

* An example sequence-set might be: *

    *
  • 11,2,3:10,20:*
  • *
  • 1:* - this is the default
  • *
*

* Some example item-names might be: *

    *
  • BODY.PEEK[HEADER]
  • *
  • 'BODY.PEEK[HEADER.FIELDS (SUBJECT)]'
  • *
  • ALL - macro equivalent to '(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)'
  • *
  • FAST - macro equivalent to '(FLAGS INTERNALDATE RFC822.SIZE)'
  • *
  • FULL - macro equivalent to '(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)'
  • *
  • ENVELOPE X-GM-LABELS
  • *
  • '(INTERNALDATE BODY.PEEK[])' - this is the default
  • *
*

* Macro names cannot be combined with anything else; they must be used alone.
* Note that using BODY will set the \Seen flag. This is why the default uses BODY.PEEK[].
* The item name X-GM-LABELS is a Google Mail extension; it shows the labels for a message.
* For example:
* IMAPExportMbox imaps://username:password@imap.googlemail.com/messages_for_export exported.mbox 1:10,20
* IMAPExportMbox imaps://username:password@imap.googlemail.com/messages_for_export exported.mbox 3 ENVELOPE X-GM-LABELS
*

* The sequence-set is passed unmodified to the FETCH command.
* The item names are wrapped in parentheses if more than one is provided. Otherwise, the parameter is assumed to be wrapped if necessary.
* Parameters with spaces must be quoted otherwise the OS shell will normally treat them as separate parameters.
* Also the listener that writes the mailbox only captures the multi-line responses (e.g. ones that include BODY references). It does not capture the output * from FETCH commands using item names such as ENVELOPE or FLAGS that return a single line response. */ public final class IMAPExportMbox { private static class MboxListener implements IMAPChunkListener { private final BufferedWriter bufferedWriter; volatile AtomicInteger total = new AtomicInteger(); volatile String lastFetched; volatile List missingIds = new ArrayList<>(); volatile long lastSeq = -1; private final String lineSeparator; private final SimpleDateFormat DATE_FORMAT // for mbox From_ lines = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"); // e.g. INTERNALDATE "27-Oct-2013 07:43:24 +0000" // for parsing INTERNALDATE private final SimpleDateFormat IDPARSE = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z"); private final boolean printHash; private final boolean printMarker; private final boolean checkSequence; MboxListener(final BufferedWriter bufferedWriter, final String lineSeparator, final boolean printHash, final boolean printMarker, final boolean checkSequence) { this.lineSeparator = lineSeparator; this.printHash = printHash; this.printMarker = printMarker; DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); this.bufferedWriter = bufferedWriter; this.checkSequence = checkSequence; } @Override public boolean chunkReceived(final IMAP imap) { final String[] replyStrings = imap.getReplyStrings(); Date received = new Date(); final String firstLine = replyStrings[0]; Matcher m = PATID.matcher(firstLine); if (m.lookingAt()) { // found a match final String date = m.group(PATID_DATE_GROUP); try { received = IDPARSE.parse(date); } catch (final ParseException e) { System.err.println(e); } } else { System.err.println("No timestamp found in: " + firstLine + " - using current time"); } String replyTo = "MAILER-DAEMON"; // default for (int i = 1; i < replyStrings.length - 1; i++) { final String line = replyStrings[i]; if (line.startsWith("Return-Path: ")) { final String[] parts = line.split(" ", 2); if (!parts[1].equals("<>")) {// Don't replace default with blank replyTo = parts[1]; if (replyTo.startsWith("<")) { if (replyTo.endsWith(">")) { replyTo = replyTo.substring(1, replyTo.length() - 1); // drop <> wrapper } else { System.err.println("Unexpected Return-path: '" + line + "' in " + firstLine); } } } break; } } try { // Add initial mbox header line bufferedWriter.append("From "); bufferedWriter.append(replyTo); bufferedWriter.append(' '); bufferedWriter.append(DATE_FORMAT.format(received)); bufferedWriter.append(lineSeparator); // Debug bufferedWriter.append("X-IMAP-Response: ").append(firstLine).append(lineSeparator); if (printMarker) { System.err.println("[" + total + "] " + firstLine); } // Skip first and last lines for (int i = 1; i < replyStrings.length - 1; i++) { final String line = replyStrings[i]; if (startsWith(line, PATFROM)) { bufferedWriter.append('>'); // Escape a From_ line } bufferedWriter.append(line); bufferedWriter.append(lineSeparator); } // The last line ends with the trailing closing ")" which needs to be stripped final String lastLine = replyStrings[replyStrings.length - 1]; final int lastLength = lastLine.length(); if (lastLength > 1) { // there's some content, we need to save it bufferedWriter.append(lastLine, 0, lastLength - 1); bufferedWriter.append(lineSeparator); } bufferedWriter.append(lineSeparator); // blank line between entries } catch (final IOException e) { e.printStackTrace(); throw new RuntimeException(e); // chunkReceived cannot throw a checked Exception } lastFetched = firstLine; total.incrementAndGet(); if (checkSequence) { m = PATSEQ.matcher(firstLine); if (m.lookingAt()) { // found a match final long msgSeq = Long.parseLong(m.group(PATSEQ_SEQUENCE_GROUP)); // Cannot fail to parse if (lastSeq != -1) { final long missing = msgSeq - lastSeq - 1; if (missing != 0) { for (long j = lastSeq + 1; j < msgSeq; j++) { missingIds.add(String.valueOf(j)); } System.err.println("*** Sequence error: current=" + msgSeq + " previous=" + lastSeq + " Missing=" + missing); } } lastSeq = msgSeq; } } if (printHash) { System.err.print("."); } return true; } public void close() throws IOException { if (bufferedWriter != null) { bufferedWriter.close(); } } } private static final String CRLF = "\r\n"; private static final String LF = "\n"; private static final String EOL_DEFAULT = System.lineSeparator(); private static final Pattern PATFROM = Pattern.compile(">*From "); // unescaped From_ // e.g. * nnn (INTERNALDATE "27-Oct-2013 07:43:24 +0000" BODY[] {nn} ...) private static final Pattern PATID = // INTERNALDATE Pattern.compile(".*INTERNALDATE \"(\\d\\d-\\w{3}-\\d{4} \\d\\d:\\d\\d:\\d\\d [+-]\\d+)\""); private static final int PATID_DATE_GROUP = 1; private static final Pattern PATSEQ = Pattern.compile("\\* (\\d+) "); // Sequence number private static final int PATSEQ_SEQUENCE_GROUP = 1; // e.g. * 382 EXISTS private static final Pattern PATEXISTS = Pattern.compile("\\* (\\d+) EXISTS"); // Response from SELECT // AAAC NO [TEMPFAIL] FETCH Temporary failure on server [CODE: WBL] private static final Pattern PATTEMPFAIL = Pattern.compile("[A-Z]{4} NO \\[TEMPFAIL\\] FETCH .*"); private static final int CONNECT_TIMEOUT = 10; // Seconds private static final int READ_TIMEOUT = 10; public static void main(final String[] args) throws IOException, URISyntaxException { int connect_timeout = CONNECT_TIMEOUT; int read_timeout = READ_TIMEOUT; int argIdx = 0; String eol = EOL_DEFAULT; boolean printHash = false; boolean printMarker = false; int retryWaitSecs = 0; for (argIdx = 0; argIdx < args.length; argIdx++) { if (args[argIdx].equals("-c")) { connect_timeout = Integer.parseInt(args[++argIdx]); } else if (args[argIdx].equals("-r")) { read_timeout = Integer.parseInt(args[++argIdx]); } else if (args[argIdx].equals("-R")) { retryWaitSecs = Integer.parseInt(args[++argIdx]); } else if (args[argIdx].equals("-LF")) { eol = LF; } else if (args[argIdx].equals("-CRLF")) { eol = CRLF; } else if (args[argIdx].equals("-.")) { printHash = true; } else if (args[argIdx].equals("-X")) { printMarker = true; } else { break; } } final int argCount = args.length - argIdx; if (argCount < 2) { System.err.println("Usage: IMAPExportMbox [-LF|-CRLF] [-c n] [-r n] [-R n] [-.] [-X]" + " imap[s]://user:password@host[:port]/folder/path [+|-] [sequence-set] [itemnames]"); System.err.println("\t-LF | -CRLF set end-of-line to LF or CRLF (default is the line.separator system property)"); System.err.println("\t-c connect timeout in seconds (default 10)"); System.err.println("\t-r read timeout in seconds (default 10)"); System.err.println("\t-R temporary failure retry wait in seconds (default 0; i.e. disabled)"); System.err.println("\t-. print a . for each complete message received"); System.err.println("\t-X print the X-IMAP line for each complete message received"); System.err.println("\tthe mboxfile is where the messages are stored; use '-' to write to standard output."); System.err.println("\tPrefix file name with '+' to append to the file. Prefix with '-' to allow overwrite."); System.err.println("\ta sequence-set is a list of numbers/number ranges e.g. 1,2,3-10,20:* - default 1:*"); System.err.println("\titemnames are the message data item name(s) e.g. BODY.PEEK[HEADER.FIELDS (SUBJECT)]" + " or a macro e.g. ALL - default (INTERNALDATE BODY.PEEK[])"); System.exit(1); } final String uriString = args[argIdx++]; URI uri; try { uri = URI.create(uriString); } catch (final IllegalArgumentException e) { // cannot parse the path as is; let's pull it apart and try again final Matcher m = Pattern.compile("(imaps?://[^/]+)(/.*)").matcher(uriString); if (!m.matches()) { throw e; } uri = URI.create(m.group(1)); // Just the scheme and auth parts uri = new URI(uri.getScheme(), uri.getAuthority(), m.group(2), null, null); } final String file = args[argIdx++]; String sequenceSet = argCount > 2 ? args[argIdx++] : "1:*"; final String itemNames; // Handle 0, 1 or multiple item names if (argCount > 3) { if (argCount > 4) { final StringBuilder sb = new StringBuilder(); sb.append("("); for (int i = 4; i <= argCount; i++) { if (i > 4) { sb.append(" "); } sb.append(args[argIdx++]); } sb.append(")"); itemNames = sb.toString(); } else { itemNames = args[argIdx++]; } } else { itemNames = "(INTERNALDATE BODY.PEEK[])"; } final boolean checkSequence = sequenceSet.matches("\\d+:(\\d+|\\*)"); // are we expecting a sequence? final MboxListener mboxListener; if (file.equals("-")) { mboxListener = null; } else if (file.startsWith("+")) { final File mbox = new File(file.substring(1)); System.out.println("Appending to file " + mbox); mboxListener = new MboxListener(new BufferedWriter(new FileWriter(mbox, true)), eol, printHash, printMarker, checkSequence); } else if (file.startsWith("-")) { final File mbox = new File(file.substring(1)); System.out.println("Writing to file " + mbox); mboxListener = new MboxListener(new BufferedWriter(new FileWriter(mbox, false)), eol, printHash, printMarker, checkSequence); } else { final File mboxFile = new File(file); if (mboxFile.exists() && mboxFile.length() > 0) { throw new IOException("mailbox file: " + mboxFile + " already exists and is non-empty!"); } System.out.println("Creating file " + mboxFile); mboxListener = new MboxListener(new BufferedWriter(new FileWriter(mboxFile)), eol, printHash, printMarker, checkSequence); } final String path = uri.getPath(); if (path == null || path.length() < 1) { throw new IllegalArgumentException("Invalid folderPath: '" + path + "'"); } final String folder = path.substring(1); // skip the leading / // suppress login details final PrintCommandListener listener = new PrintCommandListener(System.out, true) { @Override public void protocolReplyReceived(final ProtocolCommandEvent event) { if (event.getReplyCode() != IMAPReply.PARTIAL) { // This is dealt with by the chunk listener super.protocolReplyReceived(event); } } }; // Connect and login final IMAPClient imap = IMAPUtils.imapLogin(uri, connect_timeout * 1000, listener); String maxIndexInFolder = null; try { imap.setSoTimeout(read_timeout * 1000); if (!imap.select(folder)) { throw new IOException("Could not select folder: " + folder); } for (final String line : imap.getReplyStrings()) { maxIndexInFolder = matches(line, PATEXISTS, 1); if (maxIndexInFolder != null) { break; } } if (mboxListener != null) { imap.setChunkListener(mboxListener); } // else the command listener displays the full output without processing while (true) { final boolean ok = imap.fetch(sequenceSet, itemNames); // If the fetch failed, can we retry? if (ok || (retryWaitSecs <= 0) || (mboxListener == null) || !checkSequence) { break; } final String replyString = imap.getReplyString(); // includes EOL if (!startsWith(replyString, PATTEMPFAIL)) { throw new IOException("FETCH " + sequenceSet + " " + itemNames + " failed with " + replyString); } System.err.println("Temporary error detected, will retry in " + retryWaitSecs + "seconds"); sequenceSet = mboxListener.lastSeq + 1 + ":*"; try { Thread.sleep(retryWaitSecs * 1000); } catch (final InterruptedException e) { // ignored } } } catch (final IOException ioe) { final String count = mboxListener == null ? "?" : mboxListener.total.toString(); System.err.println("FETCH " + sequenceSet + " " + itemNames + " failed after processing " + count + " complete messages "); if (mboxListener != null) { System.err.println("Last complete response seen: " + mboxListener.lastFetched); } throw ioe; } finally { if (printHash) { System.err.println(); } if (mboxListener != null) { mboxListener.close(); final Iterator missingIds = mboxListener.missingIds.iterator(); if (missingIds.hasNext()) { final StringBuilder sb = new StringBuilder(); for (;;) { sb.append(missingIds.next()); if (!missingIds.hasNext()) { break; } sb.append(","); } System.err.println("*** Missing ids: " + sb.toString()); } } imap.logout(); imap.disconnect(); } if (mboxListener != null) { System.out.println("Processed " + mboxListener.total + " messages."); } if (maxIndexInFolder != null) { System.out.println("Folder contained " + maxIndexInFolder + " messages."); } } private static String matches(final String input, final Pattern pat, final int index) { final Matcher m = pat.matcher(input); if (m.lookingAt()) { return m.group(index); } return null; } private static boolean startsWith(final String input, final Pattern pat) { final Matcher m = pat.matcher(input); return m.lookingAt(); } } IMAPImportMbox.java000066400000000000000000000204021434047722200340020ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.net.imap.IMAPClient; /** * This is an example program demonstrating how to use the IMAP[S]Client class. This program connects to a IMAP[S] server and imports messages into the folder * from an mbox file. *

* Usage: IMAPImportMbox imap[s]://user:password@host[:port]/folder/path [selectors] *

* An example selector might be: *

    *
  • 1,2,3,7-10
  • *
  • -142986- : this is useful for retrieving messages by apmail number, which appears as From xyz-return-142986-apmail-...
  • *
*

* For example:
* IMAPImportMbox imaps://user:pass@imap.googlemail.com/imported_messages 201401.mbox 1-10,20 -142986- */ public final class IMAPImportMbox { private static final String CRLF = "\r\n"; private static final Pattern PATFROM = Pattern.compile(">+From "); // escaped From private static String getDate(final String msg) { // From SENDER Fri Sep 13 17:04:01 2019 final Pattern FROM_RE = Pattern.compile("From \\S+ +\\S+ (\\S+) ?(\\S+) (\\S+) (\\S+)"); // [Fri] Sep 13 HMS 2019 // output date: 13-Sep-2019 17:04:01 +0000 String date = null; final Matcher m = FROM_RE.matcher(msg); if (m.lookingAt()) { date = m.group(2) + "-" + m.group(1) + "-" + m.group(4) + " " + m.group(3) + " +0000"; } return date; } /** * Is at least one entry in the list contained in the string? * * @param contains the list of strings to look for * @param string the String to check against * @return true if at least one entry in the contains list is contained in the string */ private static boolean listContains(final List contains, final String string) { for (final String entry : contains) { if (string.contains(entry)) { return true; } } return false; } public static void main(final String[] args) throws IOException { if (args.length < 2) { System.err.println("Usage: IMAPImportMbox imap[s]://user:password@host[:port]/folder/path [selectors]"); System.err .println("\tWhere: a selector is a list of numbers/number ranges - 1,2,3-10" + " - or a list of strings to match in the initial From line"); System.exit(1); } final URI uri = URI.create(args[0]); final String file = args[1]; final File mbox = new File(file); if (!mbox.isFile() || !mbox.canRead()) { throw new IOException("Cannot read mailbox file: " + mbox); } final String path = uri.getPath(); if (path == null || path.length() < 1) { throw new IllegalArgumentException("Invalid folderPath: '" + path + "'"); } final String folder = path.substring(1); // skip the leading / final List contains = new ArrayList<>(); // list of strings to find final BitSet msgNums = new BitSet(); // list of message numbers for (int i = 2; i < args.length; i++) { final String arg = args[i]; if (arg.matches("\\d+(-\\d+)?(,\\d+(-\\d+)?)*")) { // number,m-n for (final String entry : arg.split(",")) { final String[] parts = entry.split("-"); if (parts.length == 2) { // m-n final int low = Integer.parseInt(parts[0]); final int high = Integer.parseInt(parts[1]); for (int j = low; j <= high; j++) { msgNums.set(j); } } else { msgNums.set(Integer.parseInt(entry)); } } } else { contains.add(arg); // not a number/number range } } // System.out.println(msgNums.toString()); // System.out.println(java.util.Arrays.toString(contains.toArray())); // Connect and login final IMAPClient imap = IMAPUtils.imapLogin(uri, 10000, null); int total = 0; int loaded = 0; try { imap.setSoTimeout(6000); final BufferedReader br = new BufferedReader(new FileReader(file)); // TODO charset? String line; final StringBuilder sb = new StringBuilder(); boolean wanted = false; // Skip any leading rubbish while ((line = br.readLine()) != null) { if (line.startsWith("From ")) { // start of message; i.e. end of previous (if any) if (process(sb, imap, folder, total)) { // process previous message (if any) loaded++; } sb.setLength(0); total++; wanted = wanted(total, line, msgNums, contains); } else if (startsWith(line, PATFROM)) { // Unescape ">+From " in body text line = line.substring(1); } // TODO process first Received: line to determine arrival date? if (wanted) { sb.append(line); sb.append(CRLF); } } br.close(); if (wanted && process(sb, imap, folder, total)) { // last message (if any) loaded++; } } catch (final IOException e) { System.out.println("Error processing msg: " + total + " " + imap.getReplyString()); e.printStackTrace(); System.exit(10); return; } finally { imap.logout(); imap.disconnect(); } System.out.println("Processed " + total + " messages, loaded " + loaded); } private static boolean process(final StringBuilder sb, final IMAPClient imap, final String folder, final int msgNum) throws IOException { final int length = sb.length(); final boolean haveMessage = length > 2; if (haveMessage) { System.out.println("MsgNum: " + msgNum + " Length " + length); sb.setLength(length - 2); // drop trailing CRLF (mbox format has trailing blank line) final String msg = sb.toString(); if (!imap.append(folder, null, getDate(msg), msg)) { throw new IOException("Failed to import message: " + msgNum + " " + imap.getReplyString()); } } return haveMessage; } private static boolean startsWith(final String input, final Pattern pat) { final Matcher m = pat.matcher(input); return m.lookingAt(); } /** * Is the message wanted? * * @param msgNum the message number * @param line the From line * @param msgNums the list of wanted message numbers * @param contains the list of strings to be contained * @return true if the message is wanted */ private static boolean wanted(final int msgNum, final String line, final BitSet msgNums, final List contains) { return (msgNums.isEmpty() && contains.isEmpty()) // no selectors || msgNums.get(msgNum) // matches message number || listContains(contains, line); // contains string } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/IMAPMail.java000066400000000000000000000051031434047722200326440ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.IOException; import java.net.URI; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.imap.IMAPClient; /** * This is an example program demonstrating how to use the IMAP[S]Client class. This program connects to a IMAP[S] server, lists its capabilities and shows the * status of the Inbox. *

* Usage: IMAPMail imap[s]://username:password@server/ *

* For example
* IMAPMail imaps://username:password@imap.mail.yahoo.com/
* or
* IMAPMail imaps://username:password@imap.googlemail.com/ */ public final class IMAPMail { public static void main(final String[] args) throws IOException { if (args.length != 1) { System.err.println("Usage: IMAPMail imap[s]://username:password@server/"); System.err.println("Connects to server; lists capabilities and shows Inbox status"); System.exit(1); } final URI uri = URI.create(args[0]); // Connect and login final IMAPClient imap = IMAPUtils.imapLogin(uri, 10000, null); // suppress login details imap.addProtocolCommandListener(new PrintCommandListener(System.out, true)); try { imap.setSoTimeout(6000); imap.capability(); imap.select("inbox"); imap.examine("inbox"); imap.status("inbox", new String[] { "MESSAGES" }); imap.list("", "*"); // Show the folders } catch (final IOException e) { System.out.println(imap.getReplyString()); e.printStackTrace(); System.exit(10); return; } finally { imap.logout(); imap.disconnect(); } } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/IMAPUtils.java000066400000000000000000000067771434047722200331040ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.IOException; import java.net.URI; import org.apache.commons.net.ProtocolCommandListener; import org.apache.commons.net.imap.IMAPClient; import org.apache.commons.net.imap.IMAPSClient; /** * Utility class for shared IMAP utilities */ class IMAPUtils { /** * Parse the URI and use the details to connect to the IMAP(S) server and login. * * @param uri the URI to use, e.g. imaps://user:pass@imap.mail.yahoo.com/folder or imaps://user:pass@imap.googlemail.com/folder * @param defaultTimeout initial timeout (in milliseconds) * @param listener for tracing protocol IO (may be null) * @return the IMAP client - connected and logged in * @throws IOException if any problems occur */ static IMAPClient imapLogin(final URI uri, final int defaultTimeout, final ProtocolCommandListener listener) throws IOException { final String userInfo = uri.getUserInfo(); if (userInfo == null) { throw new IllegalArgumentException("Missing userInfo details"); } final String[] userpass = userInfo.split(":"); if (userpass.length != 2) { throw new IllegalArgumentException("Invalid userInfo details: '" + userInfo + "'"); } final String username = userpass[0]; String password = userpass[1]; // prompt for the password if necessary password = Utils.getPassword(username, password); final IMAPClient imap; final String scheme = uri.getScheme(); if ("imaps".equalsIgnoreCase(scheme)) { System.out.println("Using secure protocol"); imap = new IMAPSClient(true); // implicit } else if ("imap".equalsIgnoreCase(scheme)) { imap = new IMAPClient(); } else { throw new IllegalArgumentException("Invalid protocol: " + scheme); } final int port = uri.getPort(); if (port != -1) { imap.setDefaultPort(port); } imap.setDefaultTimeout(defaultTimeout); if (listener != null) { imap.addProtocolCommandListener(listener); } final String server = uri.getHost(); System.out.println("Connecting to server " + server + " on " + imap.getDefaultPort()); try { imap.connect(server); System.out.println("Successfully connected"); } catch (final IOException e) { throw new RuntimeException("Could not connect to server.", e); } if (!imap.login(username, password)) { imap.disconnect(); throw new RuntimeException("Could not login to server. Check login details."); } return imap; } } POP3ExportMbox.java000066400000000000000000000162551434047722200340170ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.net.pop3.POP3Client; import org.apache.commons.net.pop3.POP3MessageInfo; import org.apache.commons.net.pop3.POP3SClient; /** * This is an example program demonstrating how to use the POP3[S]Client class. This program connects to a POP3[S] server and writes the messages to an mbox * file. *

* The code currently assumes that POP3Client decodes the POP3 data as iso-8859-1. The POP3 standard only allows for ASCII so in theory iso-8859-1 should be OK. * However it appears that actual POP3 implementations may return 8bit data that is outside the ASCII range; this may result in loss of data when the mailbox is * created. *

* See main() method for usage details */ public final class POP3ExportMbox { private static final Pattern PATFROM = Pattern.compile(">*From "); // unescaped From_ public static void main(final String[] args) { int argIdx; String file = null; for (argIdx = 0; argIdx < args.length; argIdx++) { if (!args[argIdx].equals("-F")) { break; } file = args[++argIdx]; } final int argCount = args.length - argIdx; if (argCount < 3) { System.err.println("Usage: POP3Mail [-F file/directory] [TLS [true=implicit]]"); System.exit(1); } final String arg0[] = args[argIdx++].split(":"); final String server = arg0[0]; final String username = args[argIdx++]; String password = args[argIdx++]; // prompt for the password if necessary try { password = Utils.getPassword(username, password); } catch (final IOException e1) { System.err.println("Could not retrieve password: " + e1.getMessage()); return; } final String proto = argCount > 3 ? args[argIdx++] : null; final boolean implicit = argCount > 4 && Boolean.parseBoolean(args[argIdx++]); final POP3Client pop3; if (proto != null) { System.out.println("Using secure protocol: " + proto); pop3 = new POP3SClient(proto, implicit); } else { pop3 = new POP3Client(); } final int port; if (arg0.length == 2) { port = Integer.parseInt(arg0[1]); } else { port = pop3.getDefaultPort(); } System.out.println("Connecting to server " + server + " on " + port); // We want to timeout if a response takes longer than 60 seconds pop3.setDefaultTimeout(60000); try { pop3.connect(server); } catch (final IOException e) { System.err.println("Could not connect to server."); e.printStackTrace(); return; } try { if (!pop3.login(username, password)) { System.err.println("Could not login to server. Check password."); pop3.disconnect(); return; } final POP3MessageInfo status = pop3.status(); if (status == null) { System.err.println("Could not retrieve status."); pop3.logout(); pop3.disconnect(); return; } System.out.println("Status: " + status); final int count = status.number; if (file != null) { System.out.println("Getting messages: " + count); final File mbox = new File(file); if (mbox.isDirectory()) { System.out.println("Writing dir: " + mbox); // Currently POP3Client uses iso-8859-1 for (int i = 1; i <= count; i++) { try (final OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(new File(mbox, i + ".eml")), StandardCharsets.ISO_8859_1)) { writeFile(pop3, fw, i); } } } else { System.out.println("Writing file: " + mbox); // Currently POP3Client uses iso-8859-1 try (final OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(mbox), StandardCharsets.ISO_8859_1)) { for (int i = 1; i <= count; i++) { writeMbox(pop3, fw, i); } } } } pop3.logout(); pop3.disconnect(); } catch (final IOException e) { e.printStackTrace(); return; } } private static boolean startsWith(final String input, final Pattern pat) { final Matcher m = pat.matcher(input); return m.lookingAt(); } private static void writeFile(final POP3Client pop3, final OutputStreamWriter fw, final int i) throws IOException { try (final BufferedReader r = (BufferedReader) pop3.retrieveMessage(i)) { String line; while ((line = r.readLine()) != null) { fw.write(line); fw.write("\n"); } } } private static void writeMbox(final POP3Client pop3, final OutputStreamWriter fw, final int i) throws IOException { final SimpleDateFormat DATE_FORMAT // for mbox From_ lines = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy"); final String replyTo = "MAILER-DAEMON"; // default final Date received = new Date(); try (final BufferedReader r = (BufferedReader) pop3.retrieveMessage(i)) { fw.append("From "); fw.append(replyTo); fw.append(' '); fw.append(DATE_FORMAT.format(received)); fw.append("\n"); String line; while ((line = r.readLine()) != null) { if (startsWith(line, PATFROM)) { fw.write(">"); } fw.write(line); fw.write("\n"); } fw.write("\n"); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/POP3Mail.java000066400000000000000000000130261434047722200326420ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.util.Locale; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.pop3.POP3Client; import org.apache.commons.net.pop3.POP3MessageInfo; import org.apache.commons.net.pop3.POP3SClient; /** * This is an example program demonstrating how to use the POP3[S]Client class. This program connects to a POP3[S] server and retrieves the message headers of * all the messages, printing the From: and Subject: header entries for each message. *

* See main() method for usage details */ public final class POP3Mail { public static void main(final String[] args) { if (args.length < 3) { System.err.println("Usage: POP3Mail [TLS [true=implicit]]"); System.exit(1); } final String arg0[] = args[0].split(":"); final String server = arg0[0]; final String username = args[1]; String password = args[2]; // prompt for the password if necessary try { password = Utils.getPassword(username, password); } catch (final IOException e1) { System.err.println("Could not retrieve password: " + e1.getMessage()); return; } final String proto = args.length > 3 ? args[3] : null; final boolean implicit = args.length > 4 && Boolean.parseBoolean(args[4]); final POP3Client pop3; if (proto != null) { System.out.println("Using secure protocol: " + proto); pop3 = new POP3SClient(proto, implicit); } else { pop3 = new POP3Client(); } final int port; if (arg0.length == 2) { port = Integer.parseInt(arg0[1]); } else { port = pop3.getDefaultPort(); } System.out.println("Connecting to server " + server + " on " + port); // We want to timeout if a response takes longer than 60 seconds pop3.setDefaultTimeout(60000); // suppress login details pop3.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); try { pop3.connect(server); } catch (final IOException e) { System.err.println("Could not connect to server."); e.printStackTrace(); return; } try { if (!pop3.login(username, password)) { System.err.println("Could not login to server. Check password."); pop3.disconnect(); return; } final POP3MessageInfo status = pop3.status(); if (status == null) { System.err.println("Could not retrieve status."); pop3.logout(); pop3.disconnect(); return; } System.out.println("Status: " + status); final POP3MessageInfo[] messages = pop3.listMessages(); if (messages == null) { System.err.println("Could not retrieve message list."); pop3.logout(); pop3.disconnect(); return; } if (messages.length == 0) { System.out.println("No messages"); pop3.logout(); pop3.disconnect(); return; } System.out.println("Message count: " + messages.length); for (final POP3MessageInfo msginfo : messages) { final BufferedReader reader = (BufferedReader) pop3.retrieveMessageTop(msginfo.number, 0); if (reader == null) { System.err.println("Could not retrieve message header."); pop3.logout(); pop3.disconnect(); return; } printMessageInfo(reader, msginfo.number); } pop3.logout(); pop3.disconnect(); } catch (final IOException e) { e.printStackTrace(); return; } } public static void printMessageInfo(final BufferedReader reader, final int id) throws IOException { String from = ""; String subject = ""; String line; while ((line = reader.readLine()) != null) { final String lower = line.toLowerCase(Locale.ENGLISH); if (lower.startsWith("from: ")) { from = line.substring(6).trim(); } else if (lower.startsWith("subject: ")) { subject = line.substring(9).trim(); } } System.out.println(Integer.toString(id) + " From: " + from + " Subject: " + subject); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/SMTPMail.java000066400000000000000000000107531434047722200327100ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Writer; import java.util.ArrayList; import java.util.List; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.io.Util; import org.apache.commons.net.smtp.SMTPClient; import org.apache.commons.net.smtp.SMTPReply; import org.apache.commons.net.smtp.SimpleSMTPHeader; /** * This is an example program using the SMTP package to send a message to the specified recipients. It prompts you for header information and a file name * containing the message. */ public final class SMTPMail { public static void main(final String[] args) { final String sender; final String recipient; final String subject; final String fileName; final String server; String cc; final List ccList = new ArrayList<>(); final BufferedReader stdin; FileReader fileReader = null; final Writer writer; final SimpleSMTPHeader header; final SMTPClient client; if (args.length < 1) { System.err.println("Usage: SMTPMail "); System.exit(1); } server = args[0]; stdin = new BufferedReader(new InputStreamReader(System.in)); try { System.out.print("From: "); System.out.flush(); sender = stdin.readLine(); System.out.print("To: "); System.out.flush(); recipient = stdin.readLine(); System.out.print("Subject: "); System.out.flush(); subject = stdin.readLine(); header = new SimpleSMTPHeader(sender, recipient, subject); while (true) { System.out.print("CC : "); System.out.flush(); cc = stdin.readLine(); if (cc == null || cc.isEmpty()) { break; } header.addCC(cc.trim()); ccList.add(cc.trim()); } System.out.print("Filename: "); System.out.flush(); fileName = stdin.readLine(); try { fileReader = new FileReader(fileName); } catch (final FileNotFoundException e) { System.err.println("File not found. " + e.getMessage()); } client = new SMTPClient(); client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); client.connect(server); if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) { client.disconnect(); System.err.println("SMTP server refused connection."); System.exit(1); } client.login(); client.setSender(sender); client.addRecipient(recipient); for (final String recpt : ccList) { client.addRecipient(recpt); } writer = client.sendMessageData(); if (writer != null) { writer.write(header.toString()); Util.copyReader(fileReader, writer); writer.close(); client.completePendingCommand(); } if (fileReader != null) { fileReader.close(); } client.logout(); client.disconnect(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/mail/Utils.java000066400000000000000000000051561434047722200324230ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.mail; import java.io.BufferedReader; import java.io.Console; import java.io.IOException; import java.io.InputStreamReader; import java.util.Locale; /** * Utilities for mail examples */ class Utils { /** * If the initial password is: '*' - replace it with a line read from the system console '-' - replace it with next line from STDIN 'ABCD' - if the input is * all upper case, use the field as an environment variable name * * Note: there are no guarantees that the password cannot be snooped. * * Even using the console may be subject to memory snooping, however it should be safer than the other methods. * * STDIN may require creating a temporary file which could be read by others Environment variables may be visible by using PS */ static String getPassword(final String username, String password) throws IOException { if ("-".equals(password)) { // stdin final BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); password = in.readLine(); } else if ("*".equals(password)) { // console final Console con = System.console(); // Java 1.6 if (con == null) { throw new IOException("Cannot access Console"); } final char[] pwd = con.readPassword("Password for " + username + ": "); password = new String(pwd); } else if (password.equals(password.toUpperCase(Locale.ROOT))) { // environment variable name final String tmp = System.getenv(password); if (tmp != null) { // don't overwrite if variable does not exist (just in case password is all uppers) password = tmp; } } return password; } private Utils() { // not instantiable } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/nntp/000077500000000000000000000000001434047722200305065ustar00rootroot00000000000000ArticleReader.java000066400000000000000000000065161434047722200340100ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/nntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.nntp; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.net.SocketException; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.nntp.NNTPClient; import org.apache.commons.net.nntp.NewsgroupInfo; /** * Sample program demonstrating the use of article header and body retrieval */ public class ArticleReader { public static void main(final String[] args) throws SocketException, IOException { if (args.length != 2 && args.length != 3 && args.length != 5) { System.out.println("Usage: MessageThreading [

[ ]]"); return; } final String hostname = args[0]; final String newsgroup = args[1]; // Article specifier can be numeric or Id in form final String articleSpec = args.length >= 3 ? args[2] : null; final NNTPClient client = new NNTPClient(); client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); client.connect(hostname); if (args.length == 5) { // Optional auth final String user = args[3]; final String password = args[4]; if (!client.authenticate(user, password)) { System.out.println("Authentication failed for user " + user + "!"); System.exit(1); } } final NewsgroupInfo group = new NewsgroupInfo(); client.selectNewsgroup(newsgroup, group); final BufferedReader brHdr; String line; if (articleSpec != null) { brHdr = (BufferedReader) client.retrieveArticleHeader(articleSpec); } else { final long articleNum = group.getLastArticleLong(); brHdr = client.retrieveArticleHeader(articleNum); } if (brHdr != null) { while ((line = brHdr.readLine()) != null) { System.out.println(line); } brHdr.close(); } final BufferedReader brBody; if (articleSpec != null) { brBody = (BufferedReader) client.retrieveArticleBody(articleSpec); } else { final long articleNum = group.getLastArticleLong(); brBody = client.retrieveArticleBody(articleNum); } if (brBody != null) { while ((line = brBody.readLine()) != null) { System.out.println(line); } brBody.close(); } } } ExtendedNNTPOps.java000066400000000000000000000066251434047722200342250ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/nntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.nntp; import java.io.IOException; import java.io.PrintWriter; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.nntp.Article; import org.apache.commons.net.nntp.NNTPClient; import org.apache.commons.net.nntp.NewsgroupInfo; /** * Simple class showing some of the extended commands (AUTH, XOVER, LIST ACTIVE) */ public class ExtendedNNTPOps { public static void main(final String[] args) { final ExtendedNNTPOps ops; final int argc = args.length; if (argc < 1) { System.err.println("usage: ExtendedNNTPOps nntpserver [username password]"); System.exit(1); } ops = new ExtendedNNTPOps(); ops.demo(args[0], argc >= 3 ? args[1] : null, argc >= 3 ? args[2] : null); } private final NNTPClient client; public ExtendedNNTPOps() { client = new NNTPClient(); client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); } private void demo(final String host, final String user, final String password) { try { client.connect(host); // AUTHINFO USER/AUTHINFO PASS if (user != null && password != null) { final boolean success = client.authenticate(user, password); if (success) { System.out.println("Authentication succeeded"); } else { System.out.println("Authentication failed, error =" + client.getReplyString()); } } // XOVER final NewsgroupInfo testGroup = new NewsgroupInfo(); client.selectNewsgroup("alt.test", testGroup); final long lowArticleNumber = testGroup.getFirstArticleLong(); final long highArticleNumber = lowArticleNumber + 100; final Iterable
articles = client.iterateArticleInfo(lowArticleNumber, highArticleNumber); for (final Article article : articles) { if (article.isDummy()) { // Subject will contain raw response System.out.println("Could not parse: " + article.getSubject()); } else { System.out.println(article.getSubject()); } } // LIST ACTIVE final NewsgroupInfo[] fanGroups = client.listNewsgroups("alt.fan.*"); for (final NewsgroupInfo fanGroup : fanGroups) { System.out.println(fanGroup.getNewsgroup()); } } catch (final IOException e) { e.printStackTrace(); } } } ListNewsgroups.java000066400000000000000000000052231434047722200343040ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/nntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.nntp; import java.io.IOException; import org.apache.commons.net.nntp.NNTPClient; import org.apache.commons.net.nntp.NewsgroupInfo; /** * This is a trivial example using the NNTP package to approximate the Unix newsgroups command. It merely connects to the specified news server and issues * fetches the list of newsgroups stored by the server. On servers that store a lot of newsgroups, this command can take a very long time (listing upwards of * 30,000 groups). */ public final class ListNewsgroups { public static void main(final String[] args) { if (args.length < 1) { System.err.println("Usage: newsgroups newsserver [pattern]"); return; } final NNTPClient client = new NNTPClient(); final String pattern = args.length >= 2 ? args[1] : ""; try { client.connect(args[0]); int j = 0; try { for (final String s : client.iterateNewsgroupListing(pattern)) { j++; System.out.println(s); } } catch (final IOException e1) { e1.printStackTrace(); } System.out.println(j); j = 0; for (final NewsgroupInfo n : client.iterateNewsgroups(pattern)) { j++; System.out.println(n.getNewsgroup()); } System.out.println(j); } catch (final IOException e) { e.printStackTrace(); } finally { try { if (client.isConnected()) { client.disconnect(); } } catch (final IOException e) { System.err.println("Error disconnecting from server."); e.printStackTrace(); System.exit(1); } } } } MessageThreading.java000066400000000000000000000062331434047722200345100ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/nntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.nntp; import java.io.IOException; import java.io.PrintWriter; import java.net.SocketException; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.nntp.Article; import org.apache.commons.net.nntp.NNTPClient; import org.apache.commons.net.nntp.NewsgroupInfo; import org.apache.commons.net.nntp.Threader; /** * Sample program demonstrating the use of article iteration and threading. */ public class MessageThreading { public static void main(final String[] args) throws SocketException, IOException { if (args.length != 2 && args.length != 4) { System.out.println("Usage: MessageThreading [ ]"); return; } final String hostname = args[0]; final String newsgroup = args[1]; final NNTPClient client = new NNTPClient(); client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); client.connect(hostname); if (args.length == 4) { // Optional auth final String user = args[2]; final String password = args[3]; if (!client.authenticate(user, password)) { System.out.println("Authentication failed for user " + user + "!"); System.exit(1); } } final String fmt[] = client.listOverviewFmt(); if (fmt != null) { System.out.println("LIST OVERVIEW.FMT:"); for (final String s : fmt) { System.out.println(s); } } else { System.out.println("Failed to get OVERVIEW.FMT"); } final NewsgroupInfo group = new NewsgroupInfo(); client.selectNewsgroup(newsgroup, group); final long lowArticleNumber = group.getFirstArticleLong(); final long highArticleNumber = lowArticleNumber + 5000; System.out.println("Retrieving articles between [" + lowArticleNumber + "] and [" + highArticleNumber + "]"); final Iterable
articles = client.iterateArticleInfo(lowArticleNumber, highArticleNumber); System.out.println("Building message thread tree..."); final Threader threader = new Threader(); final Article root = (Article) threader.thread(articles); Article.printThread(root, 0); } public MessageThreading() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/nntp/NNTPUtils.java000066400000000000000000000035701434047722200331560ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.nntp; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.commons.net.nntp.Article; import org.apache.commons.net.nntp.NNTPClient; /** * Some convenience methods for NNTP example classes. */ public class NNTPUtils { /** * Given an {@link NNTPClient} instance, and an integer range of messages, return an array of {@link Article} instances. * * @param client the client to use * @param lowArticleNumber low number * @param highArticleNumber high number * @return Article[] An array of Article * @throws IOException on error */ public static List
getArticleInfo(final NNTPClient client, final long lowArticleNumber, final long highArticleNumber) throws IOException { final List
articles = new ArrayList<>(); final Iterable
arts = client.iterateArticleInfo(lowArticleNumber, highArticleNumber); for (final Article article : arts) { articles.add(article); } return articles; } } PostMessage.java000066400000000000000000000117161434047722200335320ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/nntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.nntp; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Writer; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.io.Util; import org.apache.commons.net.nntp.NNTPClient; import org.apache.commons.net.nntp.NNTPReply; import org.apache.commons.net.nntp.SimpleNNTPHeader; /** * This is an example program using the NNTP package to post an article to the specified newsgroup(s). It prompts you for header information and a file name to * post. */ public final class PostMessage { public static void main(final String[] args) { final String from; final String subject; String newsgroup; final String fileName; final String server; final String organization; final String references; final BufferedReader stdin; FileReader fileReader = null; final SimpleNNTPHeader header; final NNTPClient client; if (args.length < 1) { System.err.println("Usage: post newsserver"); System.exit(1); } server = args[0]; stdin = new BufferedReader(new InputStreamReader(System.in)); try { System.out.print("From: "); System.out.flush(); from = stdin.readLine(); System.out.print("Subject: "); System.out.flush(); subject = stdin.readLine(); header = new SimpleNNTPHeader(from, subject); System.out.print("Newsgroup: "); System.out.flush(); newsgroup = stdin.readLine(); header.addNewsgroup(newsgroup); while (true) { System.out.print("Additional Newsgroup : "); System.out.flush(); newsgroup = stdin.readLine(); if (newsgroup == null) { break; } newsgroup = newsgroup.trim(); if (newsgroup.isEmpty()) { break; } header.addNewsgroup(newsgroup); } System.out.print("Organization: "); System.out.flush(); organization = stdin.readLine(); System.out.print("References: "); System.out.flush(); references = stdin.readLine(); if (organization != null && !organization.isEmpty()) { header.addHeaderField("Organization", organization); } if (references != null && !references.isEmpty()) { header.addHeaderField("References", references); } header.addHeaderField("X-Newsreader", "NetComponents"); System.out.print("Filename: "); System.out.flush(); fileName = stdin.readLine(); try { fileReader = new FileReader(fileName); } catch (final FileNotFoundException e) { System.err.println("File not found. " + e.getMessage()); System.exit(1); } client = new NNTPClient(); client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); client.connect(server); if (!NNTPReply.isPositiveCompletion(client.getReplyCode())) { client.disconnect(); System.err.println("NNTP server refused connection."); System.exit(1); } if (client.isAllowedToPost()) { final Writer writer = client.postArticle(); if (writer != null) { writer.write(header.toString()); Util.copyReader(fileReader, writer); writer.close(); client.completePendingCommand(); } } fileReader.close(); client.logout(); client.disconnect(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ntp/000077500000000000000000000000001434047722200303305ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ntp/NTPClient.java000066400000000000000000000175771434047722200330140ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.ntp; import java.io.IOException; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.text.NumberFormat; import org.apache.commons.net.ntp.NTPUDPClient; import org.apache.commons.net.ntp.NtpUtils; import org.apache.commons.net.ntp.NtpV3Packet; import org.apache.commons.net.ntp.TimeInfo; import org.apache.commons.net.ntp.TimeStamp; /** * This is an example program demonstrating how to use the NTPUDPClient class. This program sends a Datagram client request packet to a Network time Protocol * (NTP) service port on a specified server, retrieves the time, and prints it to standard output along with the fields from the NTP message header (e.g. * stratum level, reference id, poll interval, root delay, mode, ...) See the spec for details. *

* Usage: NTPClient *

*

* Example: NTPClient clock.psu.edu *

*/ public final class NTPClient { private static final NumberFormat numberFormat = new java.text.DecimalFormat("0.00"); public static void main(final String[] args) { if (args.length == 0) { System.err.println("Usage: NTPClient "); System.exit(1); } final NTPUDPClient client = new NTPUDPClient(); // We want to timeout if a response takes longer than 10 seconds client.setDefaultTimeout(10000); try { client.open(); for (final String arg : args) { System.out.println(); try { final InetAddress hostAddr = InetAddress.getByName(arg); System.out.println("> " + hostAddr.getHostName() + "/" + hostAddr.getHostAddress()); final TimeInfo info = client.getTime(hostAddr); processResponse(info); } catch (final IOException ioe) { ioe.printStackTrace(); } } } catch (final SocketException e) { e.printStackTrace(); } client.close(); } /** * Process TimeInfo object and print its details. * * @param info TimeInfo object. */ public static void processResponse(final TimeInfo info) { final NtpV3Packet message = info.getMessage(); final int stratum = message.getStratum(); final String refType; if (stratum <= 0) { refType = "(Unspecified or Unavailable)"; } else if (stratum == 1) { refType = "(Primary Reference; e.g., GPS)"; // GPS, radio clock, etc. } else { refType = "(Secondary Reference; e.g. via NTP or SNTP)"; } // stratum should be 0..15... System.out.println(" Stratum: " + stratum + " " + refType); final int version = message.getVersion(); final int li = message.getLeapIndicator(); System.out.println(" leap=" + li + ", version=" + version + ", precision=" + message.getPrecision()); System.out.println(" mode: " + message.getModeName() + " (" + message.getMode() + ")"); final int poll = message.getPoll(); // poll value typically btwn MINPOLL (4) and MAXPOLL (14) System.out.println(" poll: " + (poll <= 0 ? 1 : (int) Math.pow(2, poll)) + " seconds" + " (2 ** " + poll + ")"); final double disp = message.getRootDispersionInMillisDouble(); System.out.println(" rootdelay=" + numberFormat.format(message.getRootDelayInMillisDouble()) + ", rootdispersion(ms): " + numberFormat.format(disp)); final int refId = message.getReferenceId(); String refAddr = NtpUtils.getHostAddress(refId); String refName = null; if (refId != 0) { if (refAddr.equals("127.127.1.0")) { refName = "LOCAL"; // This is the ref address for the Local Clock } else if (stratum >= 2) { // If reference id has 127.127 prefix then it uses its own reference clock // defined in the form 127.127.clock-type.unit-num (e.g. 127.127.8.0 mode 5 // for GENERIC DCF77 AM; see refclock.htm from the NTP software distribution. if (!refAddr.startsWith("127.127")) { try { final InetAddress addr = InetAddress.getByName(refAddr); final String name = addr.getHostName(); if (name != null && !name.equals(refAddr)) { refName = name; } } catch (final UnknownHostException e) { // some stratum-2 servers sync to ref clock device but fudge stratum level higher... (e.g. 2) // ref not valid host maybe it's a reference clock name? // otherwise just show the ref IP address. refName = NtpUtils.getReferenceClock(message); } } } else if (version >= 3 && (stratum == 0 || stratum == 1)) { refName = NtpUtils.getReferenceClock(message); // refname usually have at least 3 characters (e.g. GPS, WWV, LCL, etc.) } // otherwise give up on naming the beast... } if (refName != null && refName.length() > 1) { refAddr += " (" + refName + ")"; } System.out.println(" Reference Identifier:\t" + refAddr); final TimeStamp refNtpTime = message.getReferenceTimeStamp(); System.out.println(" Reference Timestamp:\t" + refNtpTime + " " + refNtpTime.toDateString()); // Originate Time is time request sent by client (t1) final TimeStamp origNtpTime = message.getOriginateTimeStamp(); System.out.println(" Originate Timestamp:\t" + origNtpTime + " " + origNtpTime.toDateString()); final long destTimeMillis = info.getReturnTime(); // Receive Time is time request received by server (t2) final TimeStamp rcvNtpTime = message.getReceiveTimeStamp(); System.out.println(" Receive Timestamp:\t" + rcvNtpTime + " " + rcvNtpTime.toDateString()); // Transmit time is time reply sent by server (t3) final TimeStamp xmitNtpTime = message.getTransmitTimeStamp(); System.out.println(" Transmit Timestamp:\t" + xmitNtpTime + " " + xmitNtpTime.toDateString()); // Destination time is time reply received by client (t4) final TimeStamp destNtpTime = TimeStamp.getNtpTime(destTimeMillis); System.out.println(" Destination Timestamp:\t" + destNtpTime + " " + destNtpTime.toDateString()); info.computeDetails(); // compute offset/delay if not already done final Long offsetMillis = info.getOffset(); final Long delayMillis = info.getDelay(); final String delay = delayMillis == null ? "N/A" : delayMillis.toString(); final String offset = offsetMillis == null ? "N/A" : offsetMillis.toString(); System.out.println(" Roundtrip delay(ms)=" + delay + ", clock offset(ms)=" + offset); // offset in ms } } SimpleNTPServer.java000066400000000000000000000157401434047722200341250ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.ntp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import org.apache.commons.net.ntp.NtpUtils; import org.apache.commons.net.ntp.NtpV3Impl; import org.apache.commons.net.ntp.NtpV3Packet; import org.apache.commons.net.ntp.TimeStamp; /** * The SimpleNTPServer class is a UDP implementation of a server for the Network Time Protocol (NTP) version 3 as described in RFC 1305. It is a minimal NTP * server that doesn't actually adjust the time but only responds to NTP datagram requests with response sent back to originating host with info filled out * using the current clock time. To be used for debugging or testing. * * To prevent this from interfering with the actual NTP service it can be run from any local port. */ public class SimpleNTPServer implements Runnable { public static void main(final String[] args) { int port = NtpV3Packet.NTP_PORT; if (args.length != 0) { try { port = Integer.parseInt(args[0]); } catch (final NumberFormatException nfe) { nfe.printStackTrace(); } } final SimpleNTPServer timeServer = new SimpleNTPServer(port); try { timeServer.start(); } catch (final IOException e) { e.printStackTrace(); } } private int port; private volatile boolean running; private boolean started; private DatagramSocket socket; /** * Creates SimpleNTPServer listening on default NTP port. */ public SimpleNTPServer() { this(NtpV3Packet.NTP_PORT); } /** * Creates SimpleNTPServer. * * @param port the local port the server socket is bound to, or zero for a system selected free port. * @throws IllegalArgumentException if port number less than 0 */ public SimpleNTPServer(final int port) { if (port < 0) { throw new IllegalArgumentException(); } this.port = port; } /** * Connects to server socket and listen for client connections. * * @throws IOException if an I/O error occurs when creating the socket. */ public void connect() throws IOException { if (socket == null) { socket = new DatagramSocket(port); // port = 0 is bound to available free port if (port == 0) { port = socket.getLocalPort(); } System.out.println("Running NTP service on port " + port + "/UDP"); } } public int getPort() { return port; } /** * Handles incoming packet. If NTP packet is client-mode then respond to that host with a NTP response packet otherwise ignore. * * @param request incoming DatagramPacket * @param rcvTime time packet received * * @throws IOException if an I/O error occurs. */ protected void handlePacket(final DatagramPacket request, final long rcvTime) throws IOException { final NtpV3Packet message = new NtpV3Impl(); message.setDatagramPacket(request); System.out.printf("NTP packet from %s mode=%s%n", request.getAddress().getHostAddress(), NtpUtils.getModeName(message.getMode())); if (message.getMode() == NtpV3Packet.MODE_CLIENT) { final NtpV3Packet response = new NtpV3Impl(); response.setStratum(1); response.setMode(NtpV3Packet.MODE_SERVER); response.setVersion(NtpV3Packet.VERSION_3); response.setPrecision(-20); response.setPoll(0); response.setRootDelay(62); response.setRootDispersion((int) (16.51 * 65.536)); // originate time as defined in RFC-1305 (t1) response.setOriginateTimeStamp(message.getTransmitTimeStamp()); // Receive Time is time request received by server (t2) response.setReceiveTimeStamp(TimeStamp.getNtpTime(rcvTime)); response.setReferenceTime(response.getReceiveTimeStamp()); response.setReferenceId(0x4C434C00); // LCL (Undisciplined Local Clock) // Transmit time is time reply sent by server (t3) response.setTransmitTime(TimeStamp.getNtpTime(System.currentTimeMillis())); final DatagramPacket dp = response.getDatagramPacket(); dp.setPort(request.getPort()); dp.setAddress(request.getAddress()); socket.send(dp); } // otherwise if received packet is other than CLIENT mode then ignore it } /** * Returns state of whether time service is running. * * @return true if time service is running */ public boolean isRunning() { return running; } /** * Returns state of whether time service is running. * * @return true if time service is running */ public boolean isStarted() { return started; } /** * Main method to service client connections. */ @Override public void run() { running = true; final byte buffer[] = new byte[48]; final DatagramPacket request = new DatagramPacket(buffer, buffer.length); do { try { socket.receive(request); final long rcvTime = System.currentTimeMillis(); handlePacket(request, rcvTime); } catch (final IOException e) { if (running) { e.printStackTrace(); } // otherwise socket thrown exception during shutdown } } while (running); } /** * Starts time service and provide time to client connections. * * @throws IOException if an I/O error occurs when creating the socket. */ public void start() throws IOException { if (socket == null) { connect(); } if (!started) { started = true; new Thread(this).start(); } } /** * Closes server socket and stop listening. */ public void stop() { running = false; if (socket != null) { socket.close(); // force closing of the socket socket = null; } started = false; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/ntp/TimeClient.java000066400000000000000000000055711434047722200332400ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.ntp; import java.io.IOException; import java.net.InetAddress; import org.apache.commons.net.time.TimeTCPClient; import org.apache.commons.net.time.TimeUDPClient; /** * This is an example program demonstrating how to use the TimeTCPClient and TimeUDPClient classes. This program connects to the default time service port of a * specified server, retrieves the time, and prints it to standard output. See the spec for * details. The default is to use the TCP port. Use the -udp flag to use the UDP port. *

* Usage: TimeClient [-udp] *

*/ public final class TimeClient { public static void main(final String[] args) { if (args.length == 1) { try { timeTCP(args[0]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else if (args.length == 2 && args[0].equals("-udp")) { try { timeUDP(args[1]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else { System.err.println("Usage: TimeClient [-udp] "); System.exit(1); } } public static void timeTCP(final String host) throws IOException { final TimeTCPClient client = new TimeTCPClient(); try { // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.connect(host); System.out.println(client.getDate()); } finally { client.disconnect(); } } public static void timeUDP(final String host) throws IOException { final TimeUDPClient client = new TimeUDPClient(); // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.open(); System.out.println(client.getDate(InetAddress.getByName(host))); client.close(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/package-info.java000066400000000000000000000017011434047722200327150ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Example classes. *

* These do not form part of the public API and may change without notice. */ package org.apache.commons.net.examples;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/telnet/000077500000000000000000000000001434047722200310225ustar00rootroot00000000000000TelnetClientExample.java000066400000000000000000000303361434047722200355210ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.telnet; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.StringTokenizer; import org.apache.commons.net.telnet.EchoOptionHandler; import org.apache.commons.net.telnet.InvalidTelnetOptionException; import org.apache.commons.net.telnet.SimpleOptionHandler; import org.apache.commons.net.telnet.SuppressGAOptionHandler; import org.apache.commons.net.telnet.TelnetClient; import org.apache.commons.net.telnet.TelnetNotificationHandler; import org.apache.commons.net.telnet.TerminalTypeOptionHandler; /** * This is a simple example of use of TelnetClient. An external option handler (SimpleTelnetOptionHandler) is used. Initial configuration requested by * TelnetClient will be: WILL ECHO, WILL SUPPRESS-GA, DO SUPPRESS-GA. VT100 terminal type will be subnegotiated. *

* Also, use of the sendAYT(), getLocalOptionState(), getRemoteOptionState() is demonstrated. When connected, type AYT to send an AYT command to the server and * see the result. Type OPT to see a report of the state of the first 25 options. */ public class TelnetClientExample implements Runnable, TelnetNotificationHandler { private static TelnetClient tc; /** * Main for the TelnetClientExample. * * @param args input params * @throws Exception on error */ public static void main(final String[] args) throws Exception { FileOutputStream fout = null; if (args.length < 1) { System.err.println("Usage: TelnetClientExample []"); System.exit(1); } final String remoteip = args[0]; final int remoteport; if (args.length > 1) { remoteport = Integer.parseInt(args[1]); } else { remoteport = 23; } try { fout = new FileOutputStream("spy.log", true); } catch (final IOException e) { System.err.println("Exception while opening the spy file: " + e.getMessage()); } tc = new TelnetClient(); final TerminalTypeOptionHandler ttopt = new TerminalTypeOptionHandler("VT100", false, false, true, false); final EchoOptionHandler echoopt = new EchoOptionHandler(true, false, true, false); final SuppressGAOptionHandler gaopt = new SuppressGAOptionHandler(true, true, true, true); try { tc.addOptionHandler(ttopt); tc.addOptionHandler(echoopt); tc.addOptionHandler(gaopt); } catch (final InvalidTelnetOptionException e) { System.err.println("Error registering option handlers: " + e.getMessage()); } while (true) { boolean end_loop = false; try { tc.connect(remoteip, remoteport); final Thread reader = new Thread(new TelnetClientExample()); tc.registerNotifHandler(new TelnetClientExample()); System.out.println("TelnetClientExample"); System.out.println("Type AYT to send an AYT telnet command"); System.out.println("Type OPT to print a report of status of options (0-24)"); System.out.println("Type REGISTER to register a new SimpleOptionHandler"); System.out.println("Type UNREGISTER to unregister an OptionHandler"); System.out.println("Type SPY to register the spy (connect to port 3333 to spy)"); System.out.println("Type UNSPY to stop spying the connection"); System.out.println("Type ^[A-Z] to send the control character; use ^^ to send ^"); reader.start(); final OutputStream outstr = tc.getOutputStream(); final byte[] buff = new byte[1024]; int ret_read = 0; do { try { ret_read = System.in.read(buff); if (ret_read > 0) { final String line = new String(buff, 0, ret_read); // deliberate use of default charset if (line.startsWith("AYT")) { try { System.out.println("Sending AYT"); System.out.println("AYT response:" + tc.sendAYT(5000)); } catch (final IOException e) { System.err.println("Exception waiting AYT response: " + e.getMessage()); } } else if (line.startsWith("OPT")) { System.out.println("Status of options:"); for (int ii = 0; ii < 25; ii++) { System.out.println("Local Option " + ii + ":" + tc.getLocalOptionState(ii) + " Remote Option " + ii + ":" + tc.getRemoteOptionState(ii)); } } else if (line.startsWith("REGISTER")) { final StringTokenizer st = new StringTokenizer(new String(buff)); try { st.nextToken(); final int opcode = Integer.parseInt(st.nextToken()); final boolean initlocal = Boolean.parseBoolean(st.nextToken()); final boolean initremote = Boolean.parseBoolean(st.nextToken()); final boolean acceptlocal = Boolean.parseBoolean(st.nextToken()); final boolean acceptremote = Boolean.parseBoolean(st.nextToken()); final SimpleOptionHandler opthand = new SimpleOptionHandler(opcode, initlocal, initremote, acceptlocal, acceptremote); tc.addOptionHandler(opthand); } catch (final Exception e) { if (e instanceof InvalidTelnetOptionException) { System.err.println("Error registering option: " + e.getMessage()); } else { System.err.println("Invalid REGISTER command."); System.err.println("Use REGISTER optcode initlocal initremote acceptlocal acceptremote"); System.err.println("(optcode is an integer.)"); System.err.println("(initlocal, initremote, acceptlocal, acceptremote are boolean)"); } } } else if (line.startsWith("UNREGISTER")) { final StringTokenizer st = new StringTokenizer(new String(buff)); try { st.nextToken(); final int opcode = Integer.parseInt(st.nextToken()); tc.deleteOptionHandler(opcode); } catch (final Exception e) { if (e instanceof InvalidTelnetOptionException) { System.err.println("Error unregistering option: " + e.getMessage()); } else { System.err.println("Invalid UNREGISTER command."); System.err.println("Use UNREGISTER optcode"); System.err.println("(optcode is an integer)"); } } } else if (line.startsWith("SPY")) { tc.registerSpyStream(fout); } else if (line.startsWith("UNSPY")) { tc.stopSpyStream(); } else if (line.matches("^\\^[A-Z^]\\r?\\n?$")) { final byte toSend = buff[1]; if (toSend == '^') { outstr.write(toSend); } else { outstr.write(toSend - 'A' + 1); } outstr.flush(); } else { try { outstr.write(buff, 0, ret_read); outstr.flush(); } catch (final IOException e) { end_loop = true; } } } } catch (final IOException e) { System.err.println("Exception while reading keyboard:" + e.getMessage()); end_loop = true; } } while (ret_read > 0 && !end_loop); try { tc.disconnect(); } catch (final IOException e) { System.err.println("Exception while connecting:" + e.getMessage()); } } catch (final IOException e) { System.err.println("Exception while connecting:" + e.getMessage()); System.exit(1); } } } /** * Callback method called when TelnetClient receives an option negotiation command. * * @param negotiation_code - type of negotiation command received (RECEIVED_DO, RECEIVED_DONT, RECEIVED_WILL, RECEIVED_WONT, RECEIVED_COMMAND) * @param option_code - code of the option negotiated */ @Override public void receivedNegotiation(final int negotiation_code, final int option_code) { String command = null; switch (negotiation_code) { case TelnetNotificationHandler.RECEIVED_DO: command = "DO"; break; case TelnetNotificationHandler.RECEIVED_DONT: command = "DONT"; break; case TelnetNotificationHandler.RECEIVED_WILL: command = "WILL"; break; case TelnetNotificationHandler.RECEIVED_WONT: command = "WONT"; break; case TelnetNotificationHandler.RECEIVED_COMMAND: command = "COMMAND"; break; default: command = Integer.toString(negotiation_code); // Should not happen break; } System.out.println("Received " + command + " for option code " + option_code); } /** * Reader thread. Reads lines from the TelnetClient and echoes them on the screen. */ @Override public void run() { final InputStream instr = tc.getInputStream(); try { final byte[] buff = new byte[1024]; int ret_read = 0; do { ret_read = instr.read(buff); if (ret_read > 0) { System.out.print(new String(buff, 0, ret_read)); } } while (ret_read >= 0); } catch (final IOException e) { System.err.println("Exception while reading socket:" + e.getMessage()); } try { tc.disconnect(); } catch (final IOException e) { System.err.println("Exception while closing telnet:" + e.getMessage()); } } } WeatherTelnet.java000066400000000000000000000043501434047722200343630ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.telnet; import java.io.IOException; import org.apache.commons.net.examples.util.IOUtil; import org.apache.commons.net.telnet.TelnetClient; /** * This is an example of a trivial use of the TelnetClient class. It connects to the weather server at the University of Michigan, um-weather.sprl.umich.edu * port 3000, and allows the user to interact with the server via standard input. You could use this example to connect to any telnet server, but it is * obviously not general purpose because it reads from standard input a line at a time, making it inconvenient for use with a remote interactive shell. The * TelnetClient class used by itself is mostly intended for automating access to telnet resources rather than interactive use. */ // This class requires the IOUtil support class! public final class WeatherTelnet { public static void main(final String[] args) { final TelnetClient telnet; telnet = new TelnetClient(); try { telnet.connect("rainmaker.wunderground.com", 3000); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, System.out); try { telnet.disconnect(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } System.exit(0); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/000077500000000000000000000000001434047722200305125ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/chargen.java000066400000000000000000000110631434047722200327650ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.net.InetAddress; import java.net.SocketException; import org.apache.commons.net.chargen.CharGenTCPClient; import org.apache.commons.net.chargen.CharGenUDPClient; /** * This is an example program demonstrating how to use the CharGenTCPClient and CharGenUDPClient classes. This program connects to the default chargen service * port of a specified server, then reads 100 lines from of generated output, writing each line to standard output, and then closes the connection. The UDP * invocation of the program sends 50 datagrams, printing the reply to each. The default is to use the TCP port. Use the -udp flag to use the UDP port. *

* Usage: chargen [-udp] */ public final class chargen { public static void chargenTCP(final String host) throws IOException { int lines = 100; String line; final CharGenTCPClient client = new CharGenTCPClient(); // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.connect(host); try (final BufferedReader chargenInput = new BufferedReader(new InputStreamReader(client.getInputStream()))) { // We assume the chargen service outputs lines, but it really doesn't // have to, so this code might actually not work if no newlines are // present. while (lines-- > 0) { if ((line = chargenInput.readLine()) == null) { break; } System.out.println(line); } } client.disconnect(); } public static void chargenUDP(final String host) throws IOException { int packets = 50; byte[] data; final InetAddress address; final CharGenUDPClient client; address = InetAddress.getByName(host); client = new CharGenUDPClient(); client.open(); // If we don't receive a return packet within 5 seconds, assume // the packet is lost. client.setSoTimeout(5000); while (packets-- > 0) { client.send(address); try { data = client.receive(); } // Here we catch both SocketException and InterruptedIOException, // because even though the JDK 1.1 docs claim that // InterruptedIOException is thrown on a timeout, it seems // SocketException is also thrown. catch (final SocketException e) { // We timed out and assume the packet is lost. System.err.println("SocketException: Timed out and dropped packet"); continue; } catch (final InterruptedIOException e) { // We timed out and assume the packet is lost. System.err.println("InterruptedIOException: Timed out and dropped packet"); continue; } System.out.write(data); System.out.flush(); } client.close(); } public static void main(final String[] args) { if (args.length == 1) { try { chargenTCP(args[0]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else if (args.length == 2 && args[0].equals("-udp")) { try { chargenUDP(args[1]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else { System.err.println("Usage: chargen [-udp] "); System.exit(1); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/daytime.java000066400000000000000000000054021434047722200330120ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.IOException; import java.net.InetAddress; import org.apache.commons.net.daytime.DaytimeTCPClient; import org.apache.commons.net.daytime.DaytimeUDPClient; /** * This is an example program demonstrating how to use the DaytimeTCP and DaytimeUDP classes. This program connects to the default daytime service port of a * specified server, retrieves the daytime, and prints it to standard output. The default is to use the TCP port. Use the -udp flag to use the UDP port. *

* Usage: daytime [-udp] */ public final class daytime { public static void daytimeTCP(final String host) throws IOException { final DaytimeTCPClient client = new DaytimeTCPClient(); // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.connect(host); System.out.println(client.getTime().trim()); client.disconnect(); } public static void daytimeUDP(final String host) throws IOException { final DaytimeUDPClient client = new DaytimeUDPClient(); // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.open(); System.out.println(client.getTime(InetAddress.getByName(host)).trim()); client.close(); } public static void main(final String[] args) { if (args.length == 1) { try { daytimeTCP(args[0]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else if (args.length == 2 && args[0].equals("-udp")) { try { daytimeUDP(args[1]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else { System.err.println("Usage: daytime [-udp] "); System.exit(1); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/echo.java000066400000000000000000000122371434047722200323000ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.SocketException; import org.apache.commons.net.echo.EchoTCPClient; import org.apache.commons.net.echo.EchoUDPClient; /** * This is an example program demonstrating how to use the EchoTCPClient and EchoUDPClient classes. This program connects to the default echo service port of a * specified server, then reads lines from standard input, writing them to the echo server, and then printing the echo. The default is to use the TCP port. Use * the -udp flag to use the UDP port. *

* Usage: echo [-udp] */ public final class echo { public static void echoTCP(final String host) throws IOException { final EchoTCPClient client = new EchoTCPClient(); final BufferedReader input; final BufferedReader echoInput; final PrintWriter echoOutput; String line; // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.connect(host); System.out.println("Connected to " + host + "."); input = new BufferedReader(new InputStreamReader(System.in)); echoOutput = new PrintWriter(new OutputStreamWriter(client.getOutputStream()), true); echoInput = new BufferedReader(new InputStreamReader(client.getInputStream())); while ((line = input.readLine()) != null) { echoOutput.println(line); System.out.println(echoInput.readLine()); } echoOutput.close(); echoInput.close(); echoInput.close(); client.disconnect(); } public static void echoUDP(final String host) throws IOException { int length, count; byte[] data; String line; final BufferedReader input; final InetAddress address; final EchoUDPClient client; input = new BufferedReader(new InputStreamReader(System.in)); address = InetAddress.getByName(host); client = new EchoUDPClient(); client.open(); // If we don't receive an echo within 5 seconds, assume the packet is lost. client.setSoTimeout(5000); System.out.println("Ready to echo to " + host + "."); // Remember, there are no guarantees about the ordering of returned // UDP packets, so there is a chance the output may be jumbled. while ((line = input.readLine()) != null) { data = line.getBytes(); client.send(data, address); count = 0; do { try { length = client.receive(data); } // Here we catch both SocketException and InterruptedIOException, // because even though the JDK 1.1 docs claim that // InterruptedIOException is thrown on a timeout, it seems // SocketException is also thrown. catch (final SocketException e) { // We timed out and assume the packet is lost. System.err.println("SocketException: Timed out and dropped packet"); break; } catch (final InterruptedIOException e) { // We timed out and assume the packet is lost. System.err.println("InterruptedIOException: Timed out and dropped packet"); break; } System.out.print(new String(data, 0, length)); count += length; } while (count < data.length); System.out.println(); } client.close(); } public static void main(final String[] args) { if (args.length == 1) { try { echoTCP(args[0]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else if (args.length == 2 && args[0].equals("-udp")) { try { echoUDP(args[1]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else { System.err.println("Usage: echo [-udp] "); System.exit(1); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/finger.java000066400000000000000000000102411434047722200326250ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import org.apache.commons.net.finger.FingerClient; /** * This is an example of how you would implement the finger command in Java using NetComponents. The Java version is much shorter. But keep in mind that the * Unix finger command reads all sorts of local files to output local finger information. This program only queries the finger daemon. *

* The -l flag is used to request long output from the server. */ public final class finger { public static void main(final String[] args) { boolean longOutput = false; int arg = 0, index; String handle, host; final FingerClient finger; InetAddress address = null; // Get flags. If an invalid flag is present, exit with usage message. while (arg < args.length && args[arg].startsWith("-")) { if (args[arg].equals("-l")) { longOutput = true; } else { System.err.println("usage: finger [-l] [[[handle][@]] ...]"); System.exit(1); } ++arg; } finger = new FingerClient(); // We want to timeout if a response takes longer than 60 seconds finger.setDefaultTimeout(60000); if (arg >= args.length) { // Finger local host try { address = InetAddress.getLocalHost(); } catch (final UnknownHostException e) { System.err.println("Error unknown host: " + e.getMessage()); System.exit(1); } try { finger.connect(address); System.out.print(finger.query(longOutput)); finger.disconnect(); } catch (final IOException e) { System.err.println("Error I/O exception: " + e.getMessage()); System.exit(1); } return; } // Finger each argument while (arg < args.length) { index = args[arg].lastIndexOf('@'); if (index == -1) { handle = args[arg]; try { address = InetAddress.getLocalHost(); } catch (final UnknownHostException e) { System.err.println("Error unknown host: " + e.getMessage()); System.exit(1); } } else { handle = args[arg].substring(0, index); host = args[arg].substring(index + 1); try { address = InetAddress.getByName(host); System.out.println("[" + address.getHostName() + "]"); } catch (final UnknownHostException e) { System.err.println("Error unknown host: " + e.getMessage()); System.exit(1); } } try { finger.connect(address); System.out.print(finger.query(longOutput, handle)); finger.disconnect(); } catch (final IOException e) { System.err.println("Error I/O exception: " + e.getMessage()); System.exit(1); } ++arg; if (arg != args.length) { System.out.print("\n"); } } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/fwhois.java000066400000000000000000000047271434047722200326660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import org.apache.commons.net.whois.WhoisClient; /** * This is an example of how you would implement the Linux fwhois command in Java using NetComponents. The Java version is much shorter. */ public final class fwhois { public static void main(final String[] args) { final int index; final String handle; final String host; InetAddress address = null; final WhoisClient whois; if (args.length != 1) { System.err.println("usage: fwhois handle[@]"); System.exit(1); } index = args[0].lastIndexOf('@'); whois = new WhoisClient(); // We want to timeout if a response takes longer than 60 seconds whois.setDefaultTimeout(60000); if (index == -1) { handle = args[0]; host = WhoisClient.DEFAULT_HOST; } else { handle = args[0].substring(0, index); host = args[0].substring(index + 1); } try { address = InetAddress.getByName(host); System.out.println("[" + address.getHostName() + "]"); } catch (final UnknownHostException e) { System.err.println("Error unknown host: " + e.getMessage()); System.exit(1); } try { whois.connect(address); System.out.print(whois.query(handle)); whois.disconnect(); } catch (final IOException e) { System.err.println("Error I/O exception: " + e.getMessage()); System.exit(1); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/rdate.java000066400000000000000000000056101434047722200324560ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.IOException; import java.net.InetAddress; import org.apache.commons.net.time.TimeTCPClient; import org.apache.commons.net.time.TimeUDPClient; /** * This is an example program demonstrating how to use the TimeTCPClient and TimeUDPClient classes. It's very similar to the simple Unix rdate command. This * program connects to the default time service port of a specified server, retrieves the time, and prints it to standard output. The default is to use the TCP * port. Use the -udp flag to use the UDP port. You can test this program by using the NIST time server at 132.163.135.130 (warning: the IP address may change). *

* Usage: rdate [-udp] */ public final class rdate { public static void main(final String[] args) { if (args.length == 1) { try { timeTCP(args[0]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else if (args.length == 2 && args[0].equals("-udp")) { try { timeUDP(args[1]); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } } else { System.err.println("Usage: rdate [-udp] "); System.exit(1); } } public static void timeTCP(final String host) throws IOException { final TimeTCPClient client = new TimeTCPClient(); // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.connect(host); System.out.println(client.getDate().toString()); client.disconnect(); } public static void timeUDP(final String host) throws IOException { final TimeUDPClient client = new TimeUDPClient(); // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.open(); System.out.println(client.getDate(InetAddress.getByName(host)).toString()); client.close(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/rexec.java000066400000000000000000000060171434047722200324670ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.IOException; import org.apache.commons.net.bsd.RExecClient; import org.apache.commons.net.examples.util.IOUtil; /** * This is an example program demonstrating how to use the RExecClient class. This program connects to an rexec server and requests that the given command be * executed on the server. It then reads input from stdin (this will be line buffered on most systems, so don't expect character at a time interactivity), * passing it to the remote process and writes the process stdout and stderr to local stdout. *

* Example: java rexec myhost myusername mypassword "ps -aux" *

* Usage: rexec */ // This class requires the IOUtil support class! public final class rexec { public static void main(final String[] args) { final String server; final String username; final String password; final String command; final RExecClient client; if (args.length != 4) { System.err.println("Usage: rexec "); System.exit(1); return; // so compiler can do proper flow control analysis } client = new RExecClient(); server = args[0]; username = args[1]; password = args[2]; command = args[3]; try { client.connect(server); } catch (final IOException e) { System.err.println("Could not connect to server."); e.printStackTrace(); System.exit(1); } try { client.rexec(username, password, command); } catch (final IOException e) { try { client.disconnect(); } catch (final IOException f) { /* ignored */} e.printStackTrace(); System.err.println("Could not execute command."); System.exit(1); } IOUtil.readWrite(client.getInputStream(), client.getOutputStream(), System.in, System.out); try { client.disconnect(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } System.exit(0); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/rlogin.java000066400000000000000000000071751434047722200326610ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.IOException; import org.apache.commons.net.bsd.RLoginClient; import org.apache.commons.net.examples.util.IOUtil; /** * This is an example program demonstrating how to use the RLoginClient class. This program connects to an rlogin daemon and begins to interactively read input * from stdin (this will be line buffered on most systems, so don't expect character at a time interactivity), passing it to the remote login process and * writing the remote stdout and stderr to local stdout. If you don't have .rhosts or hosts.equiv files set up, the rlogin daemon will prompt you for a * password. *

* On Unix systems you will not be able to use the rshell capability unless the process runs as root since only root can bind port addresses lower than 1024. *

* JVM's using green threads will likely have problems if the rlogin daemon requests a password. This program is merely a demonstration and is not suitable for * use as an application, especially given that it relies on line buffered input from System.in. The best way to run this example is probably from a Win95 dos * box into a Unix host. *

* Example: java rlogin myhost localusername remoteusername vt100 *

* Usage: rlogin */ // This class requires the IOUtil support class! public final class rlogin { public static void main(final String[] args) { final String server; final String localuser; final String remoteuser; final String terminal; final RLoginClient client; if (args.length != 4) { System.err.println("Usage: rlogin "); System.exit(1); return; // so compiler can do proper flow control analysis } client = new RLoginClient(); server = args[0]; localuser = args[1]; remoteuser = args[2]; terminal = args[3]; try { client.connect(server); } catch (final IOException e) { System.err.println("Could not connect to server."); e.printStackTrace(); System.exit(1); } try { client.rlogin(localuser, remoteuser, terminal); } catch (final IOException e) { try { client.disconnect(); } catch (final IOException f) { /* ignored */} e.printStackTrace(); System.err.println("rlogin authentication failed."); System.exit(1); } IOUtil.readWrite(client.getInputStream(), client.getOutputStream(), System.in, System.out); try { client.disconnect(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } System.exit(0); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/unix/rshell.java000066400000000000000000000063361434047722200326560ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.unix; import java.io.IOException; import org.apache.commons.net.bsd.RCommandClient; import org.apache.commons.net.examples.util.IOUtil; /** * This is an example program demonstrating how to use the RCommandClient class. This program connects to an rshell daemon and requests that the given command * be executed on the server. It then reads input from stdin (this will be line buffered on most systems, so don't expect character at a time interactivity), * passing it to the remote process and writes the process stdout and stderr to local stdout. *

* On Unix systems you will not be able to use the rshell capability unless the process runs as root since only root can bind port addresses lower than 1024. *

* Example: java rshell myhost localusername remoteusername "ps -aux" *

* Usage: rshell */ // This class requires the IOUtil support class! public final class rshell { public static void main(final String[] args) { final String server; final String localuser; final String remoteuser; final String command; final RCommandClient client; if (args.length != 4) { System.err.println("Usage: rshell "); System.exit(1); return; // so compiler can do proper flow control analysis } client = new RCommandClient(); server = args[0]; localuser = args[1]; remoteuser = args[2]; command = args[3]; try { client.connect(server); } catch (final IOException e) { System.err.println("Could not connect to server."); e.printStackTrace(); System.exit(1); } try { client.rcommand(localuser, remoteuser, command); } catch (final IOException e) { try { client.disconnect(); } catch (final IOException f) { /* ignored */} e.printStackTrace(); System.err.println("Could not execute command."); System.exit(1); } IOUtil.readWrite(client.getInputStream(), client.getOutputStream(), System.in, System.out); try { client.disconnect(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } System.exit(0); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/util/000077500000000000000000000000001434047722200305045ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/examples/util/IOUtil.java000066400000000000000000000053311434047722200325160ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.net.io.Util; import org.apache.commons.net.util.NetConstants; /** * This is a utility class providing a reader/writer capability required by the weatherTelnet, rexec, rshell, and rlogin example programs. The only point of the * class is to hold the static method readWrite which spawns a reader thread and a writer thread. The reader thread reads from a local input source (presumably * stdin) and writes the data to a remote output destination. The writer thread reads from a remote input source and writes to a local output destination. The * threads terminate when the remote input source closes. */ public final class IOUtil { public static void readWrite(final InputStream remoteInput, final OutputStream remoteOutput, final InputStream localInput, final OutputStream localOutput) { final Thread reader; final Thread writer; reader = new Thread(() -> { int ch; try { while (!Thread.interrupted() && (ch = localInput.read()) != NetConstants.EOS) { remoteOutput.write(ch); remoteOutput.flush(); } } catch (final IOException e) { // e.printStackTrace(); } }); writer = new Thread(() -> { try { Util.copyStream(remoteInput, localOutput); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } }); writer.setPriority(Thread.currentThread().getPriority() + 1); writer.start(); reader.setDaemon(true); reader.start(); try { writer.join(); reader.interrupt(); } catch (final InterruptedException e) { // Ignored } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/finger/000077500000000000000000000000001434047722200271635ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/finger/FingerClient.java000066400000000000000000000157031434047722200324050ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.finger; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.apache.commons.net.SocketClient; import org.apache.commons.net.util.Charsets; /** * The FingerClient class implements the client side of the Internet Finger Protocol defined in RFC 1288. To finger a host you create a FingerClient instance, * connect to the host, query the host, and finally disconnect from the host. If the finger service you want to query is on a non-standard port, connect to the * host at that port. Here's a sample use: * *

 * FingerClient finger;
 *
 * finger = new FingerClient();
 *
 * try {
 *     finger.connect("foo.bar.com");
 *     System.out.println(finger.query("foobar", false));
 *     finger.disconnect();
 * } catch (IOException e) {
 *     System.err.println("Error I/O exception: " + e.getMessage());
 *     return;
 * }
 * 
* */ public class FingerClient extends SocketClient { /** * The default FINGER port. Set to 79 according to RFC 1288. */ public static final int DEFAULT_PORT = 79; private static final String LONG_FLAG = "/W "; private final transient char[] buffer = new char[1024]; /** * The default FingerClient constructor. Initializes the default port to DEFAULT_PORT . */ public FingerClient() { setDefaultPort(DEFAULT_PORT); } /** * Fingers the connected host and returns the input stream from the network connection of the finger query. This is equivalent to calling * getInputStream(longOutput, ""). You must first connect to a finger server before calling this method, and you should disconnect after finishing reading * the stream. * * @param longOutput Set to true if long output is requested, false if not. * @return The InputStream of the network connection of the finger query. Can be read to obtain finger results. * @throws IOException If an I/O error during the operation. */ public InputStream getInputStream(final boolean longOutput) throws IOException { return getInputStream(longOutput, ""); } /** * Fingers a user and returns the input stream from the network connection of the finger query. You must first connect to a finger server before calling * this method, and you should disconnect after finishing reading the stream. * * @param longOutput Set to true if long output is requested, false if not. * @param username The name of the user to finger. * @return The InputStream of the network connection of the finger query. Can be read to obtain finger results. * @throws IOException If an I/O error during the operation. */ public InputStream getInputStream(final boolean longOutput, final String username) throws IOException { return getInputStream(longOutput, username, null); } /** * Fingers a user and returns the input stream from the network connection of the finger query. You must first connect to a finger server before calling * this method, and you should disconnect after finishing reading the stream. * * @param longOutput Set to true if long output is requested, false if not. * @param username The name of the user to finger. * @param encoding the character encoding that should be used for the query, null for the platform's default encoding * @return The InputStream of the network connection of the finger query. Can be read to obtain finger results. * @throws IOException If an I/O error during the operation. */ public InputStream getInputStream(final boolean longOutput, final String username, final String encoding) throws IOException { final DataOutputStream output; final StringBuilder buffer = new StringBuilder(64); if (longOutput) { buffer.append(LONG_FLAG); } buffer.append(username); buffer.append(SocketClient.NETASCII_EOL); // Note: Charsets.toCharset() returns the platform default for null input final byte[] encodedQuery = buffer.toString().getBytes(Charsets.toCharset(encoding).name()); // Java 1.6 can use // charset directly output = new DataOutputStream(new BufferedOutputStream(_output_, 1024)); output.write(encodedQuery, 0, encodedQuery.length); output.flush(); return _input_; } /** * Fingers the connected host and returns the output as a String. You must first connect to a finger server before calling this method, and you should * disconnect afterward. This is equivalent to calling query(longOutput, "") . * * @param longOutput Set to true if long output is requested, false if not. * @return The result of the finger query. * @throws IOException If an I/O error occurs while reading the socket. */ public String query(final boolean longOutput) throws IOException { return query(longOutput, ""); } /** * Fingers a user at the connected host and returns the output as a String. You must first connect to a finger server before calling this method, and you * should disconnect afterward. * * @param longOutput Set to true if long output is requested, false if not. * @param username The name of the user to finger. * @return The result of the finger query. * @throws IOException If an I/O error occurs while reading the socket. */ public String query(final boolean longOutput, final String username) throws IOException { int read; final StringBuilder result = new StringBuilder(buffer.length); try (final BufferedReader input = new BufferedReader(new InputStreamReader(getInputStream(longOutput, username), getCharset()))) { while (true) { read = input.read(buffer, 0, buffer.length); if (read <= 0) { break; } result.append(buffer, 0, read); } } return result.toString(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/finger/package-info.java000066400000000000000000000015531434047722200323560ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Finger implementation */ package org.apache.commons.net.finger;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/000077500000000000000000000000001434047722200265025ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/Configurable.java000066400000000000000000000024411434047722200317460ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; /** * This interface adds the aspect of configurability by means of a supplied FTPClientConfig object to other classes in the system, especially listing parsers. */ public interface Configurable { /** * @param config the object containing the configuration data * @throws IllegalArgumentException if the elements of the config are somehow inadequate to configure the Configurable object. */ void configure(FTPClientConfig config); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/DurationUtils.java000066400000000000000000000031351434047722200321550ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.time.Duration; /** Temporary until Commons Lang 3.12.0. */ class DurationUtils { /** * Tests whether the given Duration is positive (>0). * * @param duration the value to test * @return whether the given Duration is positive (>0). */ static boolean isPositive(final Duration duration) { return duration != null && !duration.isNegative() && !duration.isZero(); } static int toMillisInt(final Duration duration) { final long millis = duration.toMillis(); return millis > 0 ? (int) Math.min(millis, Integer.MAX_VALUE) : (int) Math.max(millis, Integer.MIN_VALUE); } static Duration zeroIfNull(final Duration controlIdle) { return controlIdle == null ? Duration.ZERO : controlIdle; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTP.java000066400000000000000000002356031434047722200300070ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; import org.apache.commons.net.MalformedServerReplyException; import org.apache.commons.net.ProtocolCommandSupport; import org.apache.commons.net.SocketClient; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.util.NetConstants; /** * FTP provides the basic the functionality necessary to implement your own FTP client. It extends org.apache.commons.net.SocketClient since extending * TelnetClient was causing unwanted behavior (like connections that did not time out properly). *

* To derive the full benefits of the FTP class requires some knowledge of the FTP protocol defined in RFC 959. However, there is no reason why you should have * to use the FTP class. The {@link org.apache.commons.net.ftp.FTPClient} class, derived from FTP, implements all the functionality required of an FTP client. * The FTP class is made public to provide access to various FTP constants and to make it easier for adventurous programmers (or those with special needs) to * interact with the FTP protocol and implement their own clients. A set of methods with names corresponding to the FTP command names are provided to facilitate * this interaction. *

* You should keep in mind that the FTP server may choose to prematurely close a connection if the client has been idle for longer than a given time period * (usually 900 seconds). The FTP class will detect a premature FTP server connection closing when it receives a * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } response to a command. When that occurs, the FTP class * method encountering that reply will throw an {@link org.apache.commons.net.ftp.FTPConnectionClosedException} . FTPConectionClosedException is a * subclass of IOException and therefore need not be caught separately, but if you are going to catch it separately, its catch block must appear * before the more general IOException catch block. When you encounter an {@link org.apache.commons.net.ftp.FTPConnectionClosedException} , you * must disconnect the connection with {@link #disconnect disconnect() } to properly clean up the system resources used by FTP. Before disconnecting, you may * check the last reply code and text with {@link #getReplyCode getReplyCode }, {@link #getReplyString getReplyString }, and {@link #getReplyStrings * getReplyStrings}. You may avoid server disconnections while the client is idle by periodicaly sending NOOP commands to the server. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. * * @see FTPClient * @see FTPConnectionClosedException * @see org.apache.commons.net.MalformedServerReplyException */ public class FTP extends SocketClient { /** The default FTP data port (20). */ public static final int DEFAULT_DATA_PORT = 20; /** The default FTP control port (21). */ public static final int DEFAULT_PORT = 21; /** * A constant used to indicate the file(s) being transferred should be treated as ASCII. This is the default file type. All constants ending in * FILE_TYPE are used to indicate file types. */ public static final int ASCII_FILE_TYPE = 0; /** * A constant used to indicate the file(s) being transferred should be treated as EBCDIC. Note however that there are several different EBCDIC formats. All * constants ending in FILE_TYPE are used to indicate file types. */ public static final int EBCDIC_FILE_TYPE = 1; /** * A constant used to indicate the file(s) being transferred should be treated as a binary image, i.e., no translations should be performed. All constants * ending in FILE_TYPE are used to indicate file types. */ public static final int BINARY_FILE_TYPE = 2; /** * A constant used to indicate the file(s) being transferred should be treated as a local type. All constants ending in FILE_TYPE are used to * indicate file types. */ public static final int LOCAL_FILE_TYPE = 3; /** * A constant used for text files to indicate a non-print text format. This is the default format. All constants ending in TEXT_FORMAT are used * to indicate text formatting for text transfers (both ASCII and EBCDIC). */ public static final int NON_PRINT_TEXT_FORMAT = 4; /** * A constant used to indicate a text file contains format vertical format control characters. All constants ending in TEXT_FORMAT are used to * indicate text formatting for text transfers (both ASCII and EBCDIC). */ public static final int TELNET_TEXT_FORMAT = 5; /** * A constant used to indicate a text file contains ASA vertical format control characters. All constants ending in TEXT_FORMAT are used to * indicate text formatting for text transfers (both ASCII and EBCDIC). */ public static final int CARRIAGE_CONTROL_TEXT_FORMAT = 6; /** * A constant used to indicate a file is to be treated as a continuous sequence of bytes. This is the default structure. All constants ending in * _STRUCTURE are used to indicate file structure for file transfers. */ public static final int FILE_STRUCTURE = 7; /** * A constant used to indicate a file is to be treated as a sequence of records. All constants ending in _STRUCTURE are used to indicate file * structure for file transfers. */ public static final int RECORD_STRUCTURE = 8; /** * A constant used to indicate a file is to be treated as a set of independent indexed pages. All constants ending in _STRUCTURE are used to * indicate file structure for file transfers. */ public static final int PAGE_STRUCTURE = 9; /** * A constant used to indicate a file is to be transferred as a stream of bytes. This is the default transfer mode. All constants ending in * TRANSFER_MODE are used to indicate file transfer modes. */ public static final int STREAM_TRANSFER_MODE = 10; /** * A constant used to indicate a file is to be transferred as a series of blocks. All constants ending in TRANSFER_MODE are used to indicate * file transfer modes. */ public static final int BLOCK_TRANSFER_MODE = 11; /** * A constant used to indicate a file is to be transferred as FTP compressed data. All constants ending in TRANSFER_MODE are used to indicate * file transfer modes. */ public static final int COMPRESSED_TRANSFER_MODE = 12; // We have to ensure that the protocol communication is in ASCII // but we use ISO-8859-1 just in case 8-bit characters cross // the wire. /** * The default character encoding used for communicating over an FTP control connection. The default encoding is an ASCII-compatible encoding. Some FTP * servers expect other encodings. You can change the encoding used by an FTP instance with {@link #setControlEncoding setControlEncoding}. */ public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1"; /** Length of the FTP reply code (3 alphanumerics) */ public static final int REPLY_CODE_LEN = 3; private static final String modes = "AEILNTCFRPSBC"; protected int _replyCode; protected ArrayList _replyLines; protected boolean _newReplyString; protected String _replyString; protected String _controlEncoding; /** * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and the firing of ProtocolCommandEvents. */ protected ProtocolCommandSupport _commandSupport_; /** * This is used to signal whether a block of multiline responses beginning with xxx must be terminated by the same numeric code xxx See section 4.2 of RFC * 959 for details. */ protected boolean strictMultilineParsing; /** * If this is true, then non-multiline replies must have the format: 3 digit code If false, then the 3 digit code does not have to be * followed by space See section 4.2 of RFC 959 for details. */ private boolean strictReplyParsing = true; /** * Wraps SocketClient._input_ to facilitate the reading of text from the FTP control connection. Do not access the control connection via * SocketClient._input_. This member starts with a null value, is initialized in {@link #_connectAction_}, and set to null in {@link #disconnect}. */ protected BufferedReader _controlInput_; /** * Wraps SocketClient._output_ to facilitate the writing of text to the FTP control connection. Do not access the control connection via * SocketClient._output_. This member starts with a null value, is initialized in {@link #_connectAction_}, and set to null in {@link #disconnect}. */ protected BufferedWriter _controlOutput_; /** * The default FTP constructor. Sets the default port to DEFAULT_PORT and initializes internal data structures for saving FTP reply * information. */ public FTP() { setDefaultPort(DEFAULT_PORT); _replyLines = new ArrayList<>(); _newReplyString = false; _replyString = null; _controlEncoding = DEFAULT_CONTROL_ENCODING; _commandSupport_ = new ProtocolCommandSupport(this); } /** * Get the reply, but don't pass it to command listeners. Used for keep-alive processing only. * * @since 3.0 * @throws IOException on error */ protected void __getReplyNoReport() throws IOException { getReply(false); } /** * Send a noop and get the reply without reporting to the command listener. Intended for use with keep-alive. * * @throws IOException on error * @since 3.0 */ protected void __noop() throws IOException { final String msg = buildMessage(FTPCmd.NOOP.getCommand(), null); send(msg); __getReplyNoReport(); // This may timeout } /** * Initiates control connections and gets initial reply. Initializes {@link #_controlInput_} and {@link #_controlOutput_}. */ @Override protected void _connectAction_() throws IOException { _connectAction_(null); } /** * Initiates control connections and gets initial reply. Initializes {@link #_controlInput_} and {@link #_controlOutput_}. * * @param socketIsReader the reader to reuse (if non-null) * @throws IOException on error * @since 3.4 */ protected void _connectAction_(final Reader socketIsReader) throws IOException { super._connectAction_(); // sets up _input_ and _output_ if (socketIsReader == null) { _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding())); } else { _controlInput_ = new CRLFLineReader(socketIsReader); } _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding())); if (connectTimeout > 0) { // NET-385 final int original = _socket_.getSoTimeout(); _socket_.setSoTimeout(connectTimeout); try { getReply(); // If we received code 120, we have to fetch completion reply. if (FTPReply.isPositivePreliminary(_replyCode)) { getReply(); } } catch (final SocketTimeoutException e) { final IOException ioe = new IOException("Timed out waiting for initial connect reply"); ioe.initCause(e); throw ioe; } finally { _socket_.setSoTimeout(original); } } else { getReply(); // If we received code 120, we have to fetch completion reply. if (FTPReply.isPositivePreliminary(_replyCode)) { getReply(); } } } /** * A convenience method to send the FTP ABOR command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int abor() throws IOException { return sendCommand(FTPCmd.ABOR); } /** * A convenience method to send the FTP ACCT command to the server, receive the reply, and return the reply code. * * @param account The account name to access. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int acct(final String account) throws IOException { return sendCommand(FTPCmd.ACCT, account); } /** * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code. * * @param bytes The number of bytes to allocate. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int allo(final int bytes) throws IOException { return sendCommand(FTPCmd.ALLO, Integer.toString(bytes)); } /** * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code. * * @param bytes The number of bytes to allocate. * @param recordSize The size of a record. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int allo(final int bytes, final int recordSize) throws IOException { return sendCommand(FTPCmd.ALLO, Integer.toString(bytes) + " R " + Integer.toString(recordSize)); } /** * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code. * * @param bytes The number of bytes to allocate. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int allo(final long bytes) throws IOException { return sendCommand(FTPCmd.ALLO, Long.toString(bytes)); } /** * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code. * * @param bytes The number of bytes to allocate. * @param recordSize The size of a record. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int allo(final long bytes, final int recordSize) throws IOException { return sendCommand(FTPCmd.ALLO, Long.toString(bytes) + " R " + Integer.toString(recordSize)); } /** * A convenience method to send the FTP APPE command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param pathname The pathname to use for the file when stored at the remote end of the transfer. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int appe(final String pathname) throws IOException { return sendCommand(FTPCmd.APPE, pathname); } private String buildMessage(final String command, final String args) { final StringBuilder __commandBuffer = new StringBuilder(); __commandBuffer.append(command); if (args != null) { __commandBuffer.append(' '); __commandBuffer.append(args); } __commandBuffer.append(SocketClient.NETASCII_EOL); return __commandBuffer.toString(); } /** * A convenience method to send the FTP CDUP command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int cdup() throws IOException { return sendCommand(FTPCmd.CDUP); } /** * A convenience method to send the FTP CWD command to the server, receive the reply, and return the reply code. * * @param directory The new working directory. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int cwd(final String directory) throws IOException { return sendCommand(FTPCmd.CWD, directory); } /** * A convenience method to send the FTP DELE command to the server, receive the reply, and return the reply code. * * @param pathname The pathname to delete. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int dele(final String pathname) throws IOException { return sendCommand(FTPCmd.DELE, pathname); } /** * Closes the control connection to the FTP server and sets to null some internal data so that the memory may be reclaimed by the garbage collector. The * reply text and code information from the last command is voided so that the memory it used may be reclaimed. Also sets {@link #_controlInput_} and * {@link #_controlOutput_} to null. * * @throws IOException If an error occurs while disconnecting. */ @Override public void disconnect() throws IOException { super.disconnect(); _controlInput_ = null; _controlOutput_ = null; _newReplyString = false; _replyString = null; } /** * A convenience method to send the FTP EPRT command to the server, receive the reply, and return the reply code. * * Examples: *

    *
  • EPRT |1|132.235.1.2|6275|
  • *
  • EPRT |2|1080::8:800:200C:417A|5282|
  • *
* * @see "http://www.faqs.org/rfcs/rfc2428.html" * * @param host The host owning the port. * @param port The new port. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 2.2 */ public int eprt(final InetAddress host, final int port) throws IOException { final int num; final StringBuilder info = new StringBuilder(); String h; // If IPv6, trim the zone index h = host.getHostAddress(); num = h.indexOf('%'); if (num > 0) { h = h.substring(0, num); } info.append("|"); if (host instanceof Inet4Address) { info.append("1"); } else if (host instanceof Inet6Address) { info.append("2"); } info.append("|"); info.append(h); info.append("|"); info.append(port); info.append("|"); return sendCommand(FTPCmd.EPRT, info.toString()); } /** * A convenience method to send the FTP EPSV command to the server, receive the reply, and return the reply code. Remember, it's up to you to interpret the * reply string containing the host/port information. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 2.2 */ public int epsv() throws IOException { return sendCommand(FTPCmd.EPSV); } /** * A convenience method to send the FTP FEAT command to the server, receive the reply, and return the reply code. * * @return The reply code received by the server * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 2.2 */ public int feat() throws IOException { return sendCommand(FTPCmd.FEAT); } /** * Provide command support to super-class */ @Override protected ProtocolCommandSupport getCommandSupport() { return _commandSupport_; } /** * @return The character encoding used to communicate over the control connection. */ public String getControlEncoding() { return _controlEncoding; } /** * Fetches a reply from the FTP server and returns the integer reply code. After calling this method, the actual reply text can be accessed from either * calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. Only use this method if you are implementing your own FTP * client or if you need to fetch a secondary response from the FTP server. * * @return The integer value of the reply code of the fetched FTP reply. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while receiving the server reply. */ public int getReply() throws IOException { return getReply(true); } private int getReply(final boolean reportReply) throws IOException { final int length; _newReplyString = true; _replyLines.clear(); String line = _controlInput_.readLine(); if (line == null) { throw new FTPConnectionClosedException("Connection closed without indication."); } // In case we run into an anomaly we don't want fatal index exceptions // to be thrown. length = line.length(); if (length < REPLY_CODE_LEN) { throw new MalformedServerReplyException("Truncated server reply: " + line); } String code = null; try { code = line.substring(0, REPLY_CODE_LEN); _replyCode = Integer.parseInt(code); } catch (final NumberFormatException e) { throw new MalformedServerReplyException("Could not parse response code.\nServer Reply: " + line); } _replyLines.add(line); // Check the server reply type if (length > REPLY_CODE_LEN) { final char sep = line.charAt(REPLY_CODE_LEN); // Get extra lines if message continues. if (sep == '-') { do { line = _controlInput_.readLine(); if (line == null) { throw new FTPConnectionClosedException("Connection closed without indication."); } _replyLines.add(line); // The length() check handles problems that could arise from readLine() // returning too soon after encountering a naked CR or some other // anomaly. } while (isStrictMultilineParsing() ? strictCheck(line, code) : lenientCheck(line)); } else if (isStrictReplyParsing()) { if (length == REPLY_CODE_LEN + 1) { // expecting some text throw new MalformedServerReplyException("Truncated server reply: '" + line + "'"); } if (sep != ' ') { throw new MalformedServerReplyException("Invalid server reply: '" + line + "'"); } } } else if (isStrictReplyParsing()) { throw new MalformedServerReplyException("Truncated server reply: '" + line + "'"); } if (reportReply) { fireReplyReceived(_replyCode, getReplyString()); } if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) { throw new FTPConnectionClosedException("FTP response 421 received. Server closed connection."); } return _replyCode; } /** * Returns the integer value of the reply code of the last FTP reply. You will usually only use this method after you connect to the FTP server to check * that the connection was successful since connect is of type void. * * @return The integer value of the reply code of the last FTP reply. */ public int getReplyCode() { return _replyCode; } /** * Returns the entire text of the last FTP server response exactly as it was received, including all end of line markers in NETASCII format. * * @return The entire text from the last FTP response as a String. */ public String getReplyString() { final StringBuilder buffer; if (!_newReplyString) { return _replyString; } buffer = new StringBuilder(256); for (final String line : _replyLines) { buffer.append(line); buffer.append(SocketClient.NETASCII_EOL); } _newReplyString = false; return _replyString = buffer.toString(); } /** * Returns the nth line of text from the last FTP server response as a string. The end of line markers of each are stripped from the line. * * @param index The index of the line to return, 0-based. * * @return The lines of text from the last FTP response as an array. */ String getReplyString(final int index) { return _replyLines.get(index); } /** * Returns the lines of text from the last FTP server response as an array of strings, one entry per line. The end of line markers of each are stripped from * each line. * * @return The lines of text from the last FTP response as an array. */ public String[] getReplyStrings() { return _replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY); } /** * A convenience method to send the FTP HELP command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int help() throws IOException { return sendCommand(FTPCmd.HELP); } /** * A convenience method to send the FTP HELP command to the server, receive the reply, and return the reply code. * * @param command The command name on which to request help. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int help(final String command) throws IOException { return sendCommand(FTPCmd.HELP, command); } /** * Return whether strict multiline parsing is enabled, as per RFC 959, section 4.2. * * @return True if strict, false if lenient * @since 2.0 */ public boolean isStrictMultilineParsing() { return strictMultilineParsing; } /** * Return whether strict non-multiline parsing is enabled, as per RFC 959, section 4.2. *

* The default is true, which requires the 3 digit code be followed by space and some text.
* If false, only the 3 digit code is required (as was the case for versions up to 3.5)
* * @return True if strict (default), false if additional checks are not made * @since 3.6 */ public boolean isStrictReplyParsing() { return strictReplyParsing; } // The strict check is too strong a condition because of non-conforming ftp // servers like ftp.funet.fi which sent 226 as the last line of a // 426 multi-line reply in response to ls /. We relax the condition to // test that the line starts with a digit rather than starting with // the code. private boolean lenientCheck(final String line) { return !(line.length() > REPLY_CODE_LEN && line.charAt(REPLY_CODE_LEN) != '-' && Character.isDigit(line.charAt(0))); } /** * A convenience method to send the FTP LIST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int list() throws IOException { return sendCommand(FTPCmd.LIST); } /** * A convenience method to send the FTP LIST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param pathname The pathname to list, may be {@code null} in which case the command is sent with no parameters * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int list(final String pathname) throws IOException { return sendCommand(FTPCmd.LIST, pathname); } /** * Sends the MDTM command for the given file. * * @param file name of file * @return the status * @throws IOException on error * @since 2.0 **/ public int mdtm(final String file) throws IOException { return sendCommand(FTPCmd.MDTM, file); } /** * A convenience method to send the FTP MFMT command to the server, receive the reply, and return the reply code. * * @param pathname The pathname for which mtime is to be changed * @param timeval Timestamp in yyyyMMDDhhmmss format * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 2.2 * @see http://tools.ietf.org/html/draft-somers-ftp-mfxx-04 **/ public int mfmt(final String pathname, final String timeval) throws IOException { return sendCommand(FTPCmd.MFMT, timeval + " " + pathname); } /** * A convenience method to send the FTP MKD command to the server, receive the reply, and return the reply code. * * @param pathname The pathname of the new directory to create. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int mkd(final String pathname) throws IOException { return sendCommand(FTPCmd.MKD, pathname); } /** * A convenience method to send the FTP MLSD command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 3.0 */ public int mlsd() throws IOException { return sendCommand(FTPCmd.MLSD); } /** * A convenience method to send the FTP MLSD command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param path the path to report on * @return The reply code received from the server, may be {@code null} in which case the command is sent with no parameters * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 3.0 */ public int mlsd(final String path) throws IOException { return sendCommand(FTPCmd.MLSD, path); } /** * A convenience method to send the FTP MLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 3.0 */ public int mlst() throws IOException { return sendCommand(FTPCmd.MLST); } /** * A convenience method to send the FTP MLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param path the path to report on * @return The reply code received from the server, may be {@code null} in which case the command is sent with no parameters * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 3.0 */ public int mlst(final String path) throws IOException { return sendCommand(FTPCmd.MLST, path); } /** * A convenience method to send the FTP MODE command to the server, receive the reply, and return the reply code. * * @param mode The transfer mode to use (one of the TRANSFER_MODE constants). * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int mode(final int mode) throws IOException { return sendCommand(FTPCmd.MODE, modes.substring(mode, mode + 1)); } /** * A convenience method to send the FTP NLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int nlst() throws IOException { return sendCommand(FTPCmd.NLST); } /** * A convenience method to send the FTP NLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param pathname The pathname to list, may be {@code null} in which case the command is sent with no parameters * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int nlst(final String pathname) throws IOException { return sendCommand(FTPCmd.NLST, pathname); } /** * A convenience method to send the FTP NOOP command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int noop() throws IOException { return sendCommand(FTPCmd.NOOP); } /** * A convenience method to send the FTP PASS command to the server, receive the reply, and return the reply code. * * @param password The plain text password of the username being logged into. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int pass(final String password) throws IOException { return sendCommand(FTPCmd.PASS, password); } /** * A convenience method to send the FTP PASV command to the server, receive the reply, and return the reply code. Remember, it's up to you to interpret the * reply string containing the host/port information. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int pasv() throws IOException { return sendCommand(FTPCmd.PASV); } /** * A convenience method to send the FTP PORT command to the server, receive the reply, and return the reply code. * * @param host The host owning the port. * @param port The new port. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int port(final InetAddress host, final int port) throws IOException { int num; final StringBuilder info = new StringBuilder(24); info.append(host.getHostAddress().replace('.', ',')); num = port >>> 8; info.append(','); info.append(num); info.append(','); num = port & 0xff; info.append(num); return sendCommand(FTPCmd.PORT, info.toString()); } /** * A convenience method to send the FTP PWD command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int pwd() throws IOException { return sendCommand(FTPCmd.PWD); } /** * A convenience method to send the FTP QUIT command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int quit() throws IOException { return sendCommand(FTPCmd.QUIT); } /** * A convenience method to send the FTP REIN command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int rein() throws IOException { return sendCommand(FTPCmd.REIN); } /** * A convenience method to send the FTP REST command to the server, receive the reply, and return the reply code. * * @param marker The marker at which to restart a transfer. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int rest(final String marker) throws IOException { return sendCommand(FTPCmd.REST, marker); } /** * A convenience method to send the FTP RETR command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param pathname The pathname of the file to retrieve. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int retr(final String pathname) throws IOException { return sendCommand(FTPCmd.RETR, pathname); } /** * A convenience method to send the FTP RMD command to the server, receive the reply, and return the reply code. * * @param pathname The pathname of the directory to remove. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int rmd(final String pathname) throws IOException { return sendCommand(FTPCmd.RMD, pathname); } /** * A convenience method to send the FTP RNFR command to the server, receive the reply, and return the reply code. * * @param pathname The pathname to rename from. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int rnfr(final String pathname) throws IOException { return sendCommand(FTPCmd.RNFR, pathname); } /** * A convenience method to send the FTP RNTO command to the server, receive the reply, and return the reply code. * * @param pathname The pathname to rename to * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int rnto(final String pathname) throws IOException { return sendCommand(FTPCmd.RNTO, pathname); } private void send(final String message) throws IOException, FTPConnectionClosedException, SocketException { try { _controlOutput_.write(message); _controlOutput_.flush(); } catch (final SocketException e) { if (!isConnected()) { throw new FTPConnectionClosedException("Connection unexpectedly closed."); } throw e; } } /** * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. * * @param command The FTPCmd enum corresponding to the FTP command to send. * @return The integer value of the FTP reply code returned by the server in response to the command. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 3.3 */ public int sendCommand(final FTPCmd command) throws IOException { return sendCommand(command, null); } /** * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. * * @param command The FTPCmd enum corresponding to the FTP command to send. * @param args The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument. * @return The integer value of the FTP reply code returned by the server in response to the command. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 3.3 */ public int sendCommand(final FTPCmd command, final String args) throws IOException { return sendCommand(command.getCommand(), args); } /** * Sends an FTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. * * @param command The FTPCommand constant corresponding to the FTP command to send. * @return The integer value of the FTP reply code returned by the server in response to the command. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final int command) throws IOException { return sendCommand(command, null); } /** * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. * * @param command The FTPCommand constant corresponding to the FTP command to send. * @param args The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument. * @return The integer value of the FTP reply code returned by the server in response to the command. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @deprecated (3.3) Use {@link #sendCommand(FTPCmd, String)} instead */ @Deprecated public int sendCommand(final int command, final String args) throws IOException { return sendCommand(FTPCommand.getCommand(command), args); } /** * Sends an FTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. * * @param command The text representation of the FTP command to send. * @return The integer value of the FTP reply code returned by the server in response to the command. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final String command) throws IOException { return sendCommand(command, null); } /** * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. * * @param command The text representation of the FTP command to send. * @param args The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument. * @return The integer value of the FTP reply code returned by the server in response to the command. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final String command, final String args) throws IOException { if (_controlOutput_ == null) { throw new IOException("Connection is not open"); } final String message = buildMessage(command, args); send(message); fireCommandSent(command, message); return getReply(); } /** * Saves the character encoding to be used by the FTP control connection. Some FTP servers require that commands be issued in a non-ASCII encoding like * UTF-8 so that file names with multi-byte character representations (e.g, Big 8) can be specified. *

* Please note that this has to be set before the connection is established. * * @param encoding The new character encoding for the control connection. */ public void setControlEncoding(final String encoding) { _controlEncoding = encoding; } /** * Set strict multiline parsing. * * @param strictMultilineParsing the setting * @since 2.0 */ public void setStrictMultilineParsing(final boolean strictMultilineParsing) { this.strictMultilineParsing = strictMultilineParsing; } /** * Set strict non-multiline parsing. *

* If true, it requires the 3 digit code be followed by space and some text.
* If false, only the 3 digit code is required (as was the case for versions up to 3.5) *

* This should not be required by a well-behaved FTP server
* * @param strictReplyParsing the setting * @since 3.6 */ public void setStrictReplyParsing(final boolean strictReplyParsing) { this.strictReplyParsing = strictReplyParsing; } /** * A convenience method to send the FTP SITE command to the server, receive the reply, and return the reply code. * * @param parameters The site parameters to send. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int site(final String parameters) throws IOException { return sendCommand(FTPCmd.SITE, parameters); } /** * A convenience method to send the FTP SIZE command to the server, receive the reply, and return the reply code. * * @param parameters The site parameters to send. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. * @since 3.7 */ public int size(final String parameters) throws IOException { return sendCommand(FTPCmd.SIZE, parameters); } /** * A convenience method to send the FTP SMNT command to the server, receive the reply, and return the reply code. * * @param dir The directory name. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int smnt(final String dir) throws IOException { return sendCommand(FTPCmd.SMNT, dir); } /** * A convenience method to send the FTP STAT command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stat() throws IOException { return sendCommand(FTPCmd.STAT); } /** * A convenience method to send the FTP STAT command to the server, receive the reply, and return the reply code. * * @param pathname A pathname to list. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stat(final String pathname) throws IOException { return sendCommand(FTPCmd.STAT, pathname); } /** * A convenience method to send the FTP STOR command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param pathname The pathname to use for the file when stored at the remote end of the transfer. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stor(final String pathname) throws IOException { return sendCommand(FTPCmd.STOR, pathname); } /** * A convenience method to send the FTP STOU command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stou() throws IOException { return sendCommand(FTPCmd.STOU); } /** * A convenience method to send the FTP STOU command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details * for you. * * @param pathname The base pathname to use for the file when stored at the remote end of the transfer. Some FTP servers require this. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stou(final String pathname) throws IOException { return sendCommand(FTPCmd.STOU, pathname); } // The RFC-compliant multiline termination check private boolean strictCheck(final String line, final String code) { return !(line.startsWith(code) && line.charAt(REPLY_CODE_LEN) == ' '); } /** * A convenience method to send the FTP STRU command to the server, receive the reply, and return the reply code. * * @param structure The structure of the file (one of the _STRUCTURE constants). * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stru(final int structure) throws IOException { return sendCommand(FTPCmd.STRU, modes.substring(structure, structure + 1)); } /** * A convenience method to send the FTP SYST command to the server, receive the reply, and return the reply code. * * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int syst() throws IOException { return sendCommand(FTPCmd.SYST); } /** * A convenience method to send the FTP TYPE command to the server, receive the reply, and return the reply code. * * @param fileType The type of the file (one of the FILE_TYPE constants). * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int type(final int fileType) throws IOException { return sendCommand(FTPCmd.TYPE, modes.substring(fileType, fileType + 1)); } /** * A convenience method to send the FTP TYPE command for text files to the server, receive the reply, and return the reply code. * * @param fileType The type of the file (one of the FILE_TYPE constants). * @param formatOrByteSize The format of the file (one of the _FORMAT constants. In the case of LOCAL_FILE_TYPE, the byte size. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int type(final int fileType, final int formatOrByteSize) throws IOException { final StringBuilder arg = new StringBuilder(); arg.append(modes.charAt(fileType)); arg.append(' '); if (fileType == LOCAL_FILE_TYPE) { arg.append(formatOrByteSize); } else { arg.append(modes.charAt(formatOrByteSize)); } return sendCommand(FTPCmd.TYPE, arg.toString()); } /** * A convenience method to send the FTP USER command to the server, receive the reply, and return the reply code. * * @param username The username to login under. * @return The reply code received from the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int user(final String username) throws IOException { return sendCommand(FTPCmd.USER, username); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPClient.java000066400000000000000000005442761434047722200311570ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.regex.Matcher; import org.apache.commons.net.MalformedServerReplyException; import org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory; import org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory; import org.apache.commons.net.ftp.parser.MLSxEntryParser; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.io.CopyStreamAdapter; import org.apache.commons.net.io.CopyStreamEvent; import org.apache.commons.net.io.CopyStreamListener; import org.apache.commons.net.io.FromNetASCIIInputStream; import org.apache.commons.net.io.SocketOutputStream; import org.apache.commons.net.io.ToNetASCIIOutputStream; import org.apache.commons.net.io.Util; import org.apache.commons.net.util.NetConstants; /** * FTPClient encapsulates all the functionality necessary to store and retrieve files from an FTP server. This class takes care of all low level details of * interacting with an FTP server and provides a convenient higher level interface. As with all classes derived from * {@link org.apache.commons.net.SocketClient}, you must first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before * doing anything, and finally {@link org.apache.commons.net.SocketClient#disconnect disconnect } after you're completely finished interacting with the server. * Then you need to check the FTP reply code to see if the connection was successful. For example: * *

 *    FTPClient ftp = new FTPClient();
 *    FTPClientConfig config = new FTPClientConfig();
 *    config.setXXX(YYY); // change required options
 *    // for example config.setServerTimeZoneId("Pacific/Pitcairn")
 *    ftp.configure(config );
 *    boolean error = false;
 *    try {
 *      int reply;
 *      String server = "ftp.example.com";
 *      ftp.connect(server);
 *      System.out.println("Connected to " + server + ".");
 *      System.out.print(ftp.getReplyString());
 *
 *      // After connection attempt, you should check the reply code to verify
 *      // success.
 *      reply = ftp.getReplyCode();
 *
 *      if(!FTPReply.isPositiveCompletion(reply)) {
 *        ftp.disconnect();
 *        System.err.println("FTP server refused connection.");
 *        System.exit(1);
 *      }
 *      ... // transfer files
 *      ftp.logout();
 *    } catch(IOException e) {
 *      error = true;
 *      e.printStackTrace();
 *    } finally {
 *      if(ftp.isConnected()) {
 *        try {
 *          ftp.disconnect();
 *        } catch(IOException ioe) {
 *          // do nothing
 *        }
 *      }
 *      System.exit(error ? 1 : 0);
 *    }
 * 
*

* Immediately after connecting is the only real time you need to check the reply code (because connect is of type void). The convention for all the FTP command * methods in FTPClient is such that they either return a boolean value or some other value. The boolean methods return true on a successful completion reply * from the FTP server and false on a reply resulting in an error condition or failure. The methods returning a value other than boolean return a value * containing the higher level data produced by the FTP command, or null if a reply resulted in an error condition or failure. If you want to access the exact * FTP reply code causing a success or failure, you must call {@link org.apache.commons.net.ftp.FTP#getReplyCode getReplyCode } after a success or failure. *

* The default settings for FTPClient are for it to use FTP.ASCII_FILE_TYPE , FTP.NON_PRINT_TEXT_FORMAT , * FTP.STREAM_TRANSFER_MODE , and FTP.FILE_STRUCTURE . The only file types directly supported are FTP.ASCII_FILE_TYPE * and FTP.BINARY_FILE_TYPE . Because there are at least 4 different EBCDIC encodings, we have opted not to provide direct support for EBCDIC. To * transfer EBCDIC and other unsupported file types you must create your own filter InputStreams and OutputStreams and wrap them around the streams returned or * required by the FTPClient methods. FTPClient uses the {@link ToNetASCIIOutputStream NetASCII} filter streams to provide transparent handling of ASCII files. * We will consider incorporating EBCDIC support if there is enough demand. *

* FTP.NON_PRINT_TEXT_FORMAT , FTP.STREAM_TRANSFER_MODE , and FTP.FILE_STRUCTURE are the only supported formats, * transfer modes, and file structures. *

* Because the handling of sockets on different platforms can differ significantly, the FTPClient automatically issues a new PORT (or EPRT) command prior to * every transfer requiring that the server connect to the client's data port. This ensures identical problem-free behavior on Windows, Unix, and Macintosh * platforms. Additionally, it relieves programmers from having to issue the PORT (or EPRT) command themselves and dealing with platform dependent issues. *

* Additionally, for security purposes, all data connections to the client are verified to ensure that they originated from the intended party (host and port). * If a data connection is initiated by an unexpected party, the command will close the socket and throw an IOException. You may disable this behavior with * {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}. *

* You should keep in mind that the FTP server may choose to prematurely close a connection if the client has been idle for longer than a given time period * (usually 900 seconds). The FTPClient class will detect a premature FTP server connection closing when it receives a * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } response to a command. When that occurs, the FTP class * method encountering that reply will throw an {@link org.apache.commons.net.ftp.FTPConnectionClosedException} . FTPConnectionClosedException is a * subclass of IOException and therefore need not be caught separately, but if you are going to catch it separately, its catch block must appear * before the more general IOException catch block. When you encounter an {@link org.apache.commons.net.ftp.FTPConnectionClosedException} , you * must disconnect the connection with {@link #disconnect disconnect() } to properly clean up the system resources used by FTPClient. Before disconnecting, you * may check the last reply code and text with {@link org.apache.commons.net.ftp.FTP#getReplyCode getReplyCode }, * {@link org.apache.commons.net.ftp.FTP#getReplyString getReplyString }, and {@link org.apache.commons.net.ftp.FTP#getReplyStrings getReplyStrings}. You may * avoid server disconnections while the client is idle by periodically sending NOOP commands to the server. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. *

* Listing API Examples Both paged and unpaged examples of directory listings are available, as follows: *

* Unpaged (whole list) access, using a parser accessible by auto-detect: * *

 * FTPClient f = new FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = f.listFiles(directory);
 * 
*

* Paged access, using a parser not accessible by auto-detect. The class defined in the first parameter of initateListParsing should be derived from * org.apache.commons.net.FTPFileEntryParser: * *

 * FTPClient f = new FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPListParseEngine engine = f.initiateListParsing("com.whatever.YourOwnParser", directory);
 *
 * while (engine.hasNext()) {
 *     FTPFile[] files = engine.getNext(25); // "page size" you want
 *     // do whatever you want with these files, display them, etc.
 *     // expensive FTPFile objects not created until needed.
 * }
 * 
*

* Paged access, using a parser accessible by auto-detect: * *

 * FTPClient f = new FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPListParseEngine engine = f.initiateListParsing(directory);
 *
 * while (engine.hasNext()) {
 *     FTPFile[] files = engine.getNext(25); // "page size" you want
 *     // do whatever you want with these files, display them, etc.
 *     // expensive FTPFile objects not created until needed.
 * }
 * 
*

* For examples of using FTPClient on servers whose directory listings *

    *
  • use languages other than English
  • *
  • use date formats other than the American English "standard" MM d yyyy
  • *
  • are in different time zones and you need accurate timestamps for dependency checking as in Ant
  • *
* see {@link FTPClientConfig FTPClientConfig}. *

* Control channel keep-alive feature: *

* Please note: this does not apply to the methods where the user is responsible for writing or reading the data stream, i.e. * {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)} and the other xxxFileStream methods *

* During file transfers, the data connection is busy, but the control connection is idle. FTP servers know that the control connection is in use, so won't * close it through lack of activity, but it's a lot harder for network routers to know that the control and data connections are associated with each other. * Some routers may treat the control connection as idle, and disconnect it if the transfer over the data connection takes longer than the allowable idle time * for the router.
* One solution to this is to send a safe command (i.e. NOOP) over the control connection to reset the router's idle timer. This is enabled as follows: * *

 * // Set timeout to 5 minutes
 * ftpClient.setControlKeepAliveTimeout(Duration.ofMinutes(5));
 * 
* * This will cause the file upload/download methods to send a NOOP approximately every 5 minutes. The following public methods support this: *
    *
  • {@link #retrieveFile(String, OutputStream)}
  • *
  • {@link #appendFile(String, InputStream)}
  • *
  • {@link #storeFile(String, InputStream)}
  • *
  • {@link #storeUniqueFile(InputStream)}
  • *
  • {@link #storeUniqueFileStream(String)}
  • *
* This feature does not apply to the methods where the user is responsible for writing or reading the data stream, i.e. {@link #retrieveFileStream(String)} , * {@link #storeFileStream(String)} and the other xxxFileStream methods. In such cases, the user is responsible for keeping the control connection alive if * necessary. *

* The implementation currently uses a {@link CopyStreamListener} which is passed to the * {@link Util#copyStream(InputStream, OutputStream, int, long, CopyStreamListener, boolean)} method, so the timing is partially dependent on how long each * block transfer takes. *

* This keep-alive feature is optional; if it does not help or causes problems then don't use it. * * @see #FTP_SYSTEM_TYPE * @see #SYSTEM_TYPE_PROPERTIES * @see FTP * @see FTPConnectionClosedException * @see FTPFileEntryParser * @see FTPFileEntryParserFactory * @see DefaultFTPFileEntryParserFactory * @see FTPClientConfig * @see org.apache.commons.net.MalformedServerReplyException */ public class FTPClient extends FTP implements Configurable { // @since 3.0 private static class CSL implements CopyStreamListener { private final FTPClient parent; private final long idleMillis; private final int currentSoTimeoutMillis; private long lastIdleTimeMillis = System.currentTimeMillis(); private int notAcked; private int acksAcked; private int ioErrors; CSL(final FTPClient parent, final Duration idleDuration, final Duration maxWaitDuration) throws SocketException { this.idleMillis = idleDuration.toMillis(); this.parent = parent; this.currentSoTimeoutMillis = parent.getSoTimeout(); parent.setSoTimeout(DurationUtils.toMillisInt(maxWaitDuration)); } @Override public void bytesTransferred(final CopyStreamEvent event) { bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize()); } @Override public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { final long nowMillis = System.currentTimeMillis(); if (nowMillis - lastIdleTimeMillis > idleMillis) { try { parent.__noop(); acksAcked++; } catch (final SocketTimeoutException e) { notAcked++; } catch (final IOException e) { ioErrors++; // Ignored } lastIdleTimeMillis = nowMillis; } } int[] cleanUp() throws IOException { final int remain = notAcked; try { while (notAcked > 0) { parent.getReply(); // we do want to see these notAcked--; // only decrement if actually received } } catch (final SocketTimeoutException e) { // NET-584 // ignored } finally { parent.setSoTimeout(currentSoTimeoutMillis); } return new int[] { acksAcked, remain, notAcked, ioErrors }; // debug counts } } /** * Strategy interface for updating host names received from FTP server for passive NAT workaround. * * @since 3.6 */ public interface HostnameResolver { String resolve(String hostname) throws UnknownHostException; } /** * Default strategy for passive NAT workaround (site-local replies are replaced.) * * @since 3.6 */ public static class NatServerResolverImpl implements HostnameResolver { private final FTPClient client; public NatServerResolverImpl(final FTPClient client) { this.client = client; } @Override public String resolve(final String hostname) throws UnknownHostException { String newHostname = hostname; final InetAddress host = InetAddress.getByName(newHostname); // reply is a local address, but target is not - assume NAT box changed the PASV reply if (host.isSiteLocalAddress()) { final InetAddress remote = this.client.getRemoteAddress(); if (!remote.isSiteLocalAddress()) { newHostname = remote.getHostAddress(); } } return newHostname; } } private static class PropertiesSingleton { static final Properties PROPERTIES; static { final InputStream resourceAsStream = FTPClient.class.getResourceAsStream(SYSTEM_TYPE_PROPERTIES); Properties p = null; if (resourceAsStream != null) { p = new Properties(); try { p.load(resourceAsStream); } catch (final IOException e) { // Ignored } finally { try { resourceAsStream.close(); } catch (final IOException e) { // Ignored } } } PROPERTIES = p; } } /** * The system property ({@value}) which can be used to override the system type.
* If defined, the value will be used to create any automatically created parsers. * * @since 3.0 */ public static final String FTP_SYSTEM_TYPE = "org.apache.commons.net.ftp.systemType"; /** * The system property ({@value}) which can be used as the default system type.
* If defined, the value will be used if the SYST command fails. * * @since 3.1 */ public static final String FTP_SYSTEM_TYPE_DEFAULT = "org.apache.commons.net.ftp.systemType.default"; /** * The system property that defines the default for {@link #isIpAddressFromPasvResponse()}. This property, if present, configures the default for the * following: If the client receives the servers response for a PASV request, then that response will contain an IP address. If this property is true, then * the client will use that IP address, as requested by the server. This is compatible to version {@code 3.8.0}, and before. If this property is false, or * absent, then the client will ignore that IP address, and instead use the remote address of the control connection. * * @see #isIpAddressFromPasvResponse() * @see #setIpAddressFromPasvResponse(boolean) * @since 3.9.0 */ public static final String FTP_IP_ADDRESS_FROM_PASV_RESPONSE = "org.apache.commons.net.ftp.ipAddressFromPasvResponse"; /** * The name of an optional systemType properties file ({@value}), which is loaded using {@link Class#getResourceAsStream(String)}.
* The entries are the systemType (as determined by {@link FTPClient#getSystemType}) and the values are the replacement type or parserClass, which is passed * to {@link FTPFileEntryParserFactory#createFileEntryParser(String)}.
* For example: * *

     * Plan 9=Unix
     * OS410=org.apache.commons.net.ftp.parser.OS400FTPEntryParser
     * 
* * @since 3.0 */ public static final String SYSTEM_TYPE_PROPERTIES = "/systemType.properties"; /** * A constant indicating the FTP session is expecting all transfers to occur between the client (local) and server and that the server should connect to the * client's data port to initiate a data transfer. This is the default data connection mode when and FTPClient instance is created. */ public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0; /** * A constant indicating the FTP session is expecting all transfers to occur between two remote servers and that the server the client is connected to * should connect to the other server's data port to initiate a data transfer. */ public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1; /** * A constant indicating the FTP session is expecting all transfers to occur between the client (local) and server and that the server is in passive mode, * requiring the client to connect to the server's data port to initiate a transfer. */ public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2; /** * A constant indicating the FTP session is expecting all transfers to occur between two remote servers and that the server the client is connected to is in * passive mode, requiring the other server to connect to the first server's data port to initiate a data transfer. */ public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3; /** Pattern for PASV mode responses. Groups: (n,n,n,n),(n),(n) */ private static final java.util.regex.Pattern PARMS_PAT; static { PARMS_PAT = java.util.regex.Pattern.compile("(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})"); } private static Properties getOverrideProperties() { return PropertiesSingleton.PROPERTIES; } /** * Parse the pathname from a CWD reply. *

* According to RFC959 (http://www.ietf.org/rfc/rfc959.txt), it should be the same as for MKD i.e. {@code 257""[commentary]} * where any double-quotes in {@code } are doubled. Unlike MKD, the commentary is optional. *

* However, see NET-442 for an exception. * * @param reply * @return the pathname, without enclosing quotes, or the full string after the reply code and space if the syntax is invalid (i.e. enclosing quotes are * missing or embedded quotes are not doubled) */ // package protected for access by test cases static String parsePathname(final String reply) { final String param = reply.substring(REPLY_CODE_LEN + 1); if (param.startsWith("\"")) { final StringBuilder sb = new StringBuilder(); boolean quoteSeen = false; // start after initial quote for (int i = 1; i < param.length(); i++) { final char ch = param.charAt(i); if (ch == '"') { if (quoteSeen) { sb.append(ch); quoteSeen = false; } else { // don't output yet, in case doubled quoteSeen = true; } } else { if (quoteSeen) { // found lone trailing quote within string return sb.toString(); } sb.append(ch); // just another character } } if (quoteSeen) { // found lone trailing quote at end of string return sb.toString(); } } // malformed reply, return all after reply code and space return param; } private int dataConnectionMode; private Duration dataTimeout; private int passivePort; private String passiveHost; private final Random random; private int activeMinPort; private int activeMaxPort; private InetAddress activeExternalHost; /** overrides __activeExternalHost in EPRT/PORT commands. */ private InetAddress reportActiveExternalHost; /** The address to bind to on passive connections, if necessary. */ private InetAddress passiveLocalHost; private int fileType; @SuppressWarnings("unused") // fields are written, but currently not read private int fileFormat; @SuppressWarnings("unused") // field is written, but currently not read private int fileStructure; @SuppressWarnings("unused") // field is written, but currently not read private int fileTransferMode; private boolean remoteVerificationEnabled; private long restartOffset; private FTPFileEntryParserFactory parserFactory; private int bufferSize; // buffersize for buffered data streams private int sendDataSocketBufferSize; private int receiveDataSocketBufferSize; private boolean listHiddenFiles; private boolean useEPSVwithIPv4; // whether to attempt EPSV with an IPv4 connection // __systemName is a cached value that should not be referenced directly // except when assigned in getSystemName and __initDefaults. private String systemName; // __entryParser is a cached value that should not be referenced directly // except when assigned in listFiles(String, String) and __initDefaults. private FTPFileEntryParser entryParser; // Key used to create the parser; necessary to ensure that the parser type is not ignored private String entryParserKey; private FTPClientConfig configuration; // Listener used by store/retrieve methods to handle keepalive private CopyStreamListener copyStreamListener; // How long to wait before sending another control keep-alive message private Duration controlKeepAliveTimeout = Duration.ZERO; // How long to wait for keepalive message replies before continuing // Most FTP servers don't seem to support concurrent control and data connection usage private Duration controlKeepAliveReplyTimeout = Duration.ofSeconds(1); // Debug counts for NOOP acks private int[] cslDebug; /** * Enable or disable replacement of internal IP in passive mode. Default enabled using {code NatServerResolverImpl}. */ private HostnameResolver passiveNatWorkaroundStrategy = new NatServerResolverImpl(this); /** Controls the automatic server encoding detection (only UTF-8 supported). */ private boolean autodetectEncoding; /** Map of FEAT responses. If null, has not been initialized. */ private HashMap> featuresMap; private boolean ipAddressFromPasvResponse = Boolean.parseBoolean(System.getProperty(FTPClient.FTP_IP_ADDRESS_FROM_PASV_RESPONSE)); /** * Default FTPClient constructor. Creates a new FTPClient instance with the data connection mode set to ACTIVE_LOCAL_DATA_CONNECTION_MODE , * the file type set to FTP.ASCII_FILE_TYPE , the file format set to FTP.NON_PRINT_TEXT_FORMAT , the file structure set to * FTP.FILE_STRUCTURE , and the transfer mode set to FTP.STREAM_TRANSFER_MODE . *

* The list parsing auto-detect feature can be configured to use lenient future dates (short dates may be up to one day in the future) as follows: * *

     * FTPClient ftp = new FTPClient();
     * FTPClientConfig config = new FTPClientConfig();
     * config.setLenientFutureDates(true);
     * ftp.configure(config);
     * 
*/ public FTPClient() { initDefaults(); dataTimeout = Duration.ofMillis(-1); remoteVerificationEnabled = true; parserFactory = new DefaultFTPFileEntryParserFactory(); configuration = null; listHiddenFiles = false; useEPSVwithIPv4 = false; random = new Random(); passiveLocalHost = null; } @Override protected void _connectAction_() throws IOException { _connectAction_(null); } /** * @param socketIsReader the reader to reuse (if non-null) * @throws IOException on error * @since 3.4 */ @Override protected void _connectAction_(final Reader socketIsReader) throws IOException { super._connectAction_(socketIsReader); // sets up _input_ and _output_ initDefaults(); // must be after super._connectAction_(), because otherwise we get an // Exception claiming we're not connected if (autodetectEncoding) { final ArrayList oldReplyLines = new ArrayList<>(_replyLines); final int oldReplyCode = _replyCode; if (hasFeature("UTF8") || hasFeature("UTF-8")) // UTF8 appears to be the default { setControlEncoding("UTF-8"); _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding())); _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding())); } // restore the original reply (server greeting) _replyLines.clear(); _replyLines.addAll(oldReplyLines); _replyCode = oldReplyCode; _newReplyString = true; } } /** * Establishes a data connection with the FTP server, returning a Socket for the connection if successful. If a restart offset has been set with * {@link #setRestartOffset(long)}, a REST command is issued to the server with the offset as an argument before establishing the data connection. Active * mode connections also cause a local PORT command to be issued. * * @param command The int representation of the FTP command to send. * @param arg The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument. * @return A Socket corresponding to the established data connection. Null is returned if an FTP protocol error is reported at any point during the * establishment and initialization of the connection. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.3 */ protected Socket _openDataConnection_(final FTPCmd command, final String arg) throws IOException { return _openDataConnection_(command.getCommand(), arg); } /** * Establishes a data connection with the FTP server, returning a Socket for the connection if successful. If a restart offset has been set with * {@link #setRestartOffset(long)}, a REST command is issued to the server with the offset as an argument before establishing the data connection. Active * mode connections also cause a local PORT command to be issued. * * @deprecated (3.3) Use {@link #_openDataConnection_(FTPCmd, String)} instead * @param command The int representation of the FTP command to send. * @param arg The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument. * @return A Socket corresponding to the established data connection. Null is returned if an FTP protocol error is reported at any point during the * establishment and initialization of the connection. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ @Deprecated protected Socket _openDataConnection_(final int command, final String arg) throws IOException { return _openDataConnection_(FTPCommand.getCommand(command), arg); } /** * Establishes a data connection with the FTP server, returning a Socket for the connection if successful. If a restart offset has been set with * {@link #setRestartOffset(long)}, a REST command is issued to the server with the offset as an argument before establishing the data connection. Active * mode connections also cause a local PORT command to be issued. * * @param command The text representation of the FTP command to send. * @param arg The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument. * @return A Socket corresponding to the established data connection. Null is returned if an FTP protocol error is reported at any point during the * establishment and initialization of the connection. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.1 */ protected Socket _openDataConnection_(final String command, final String arg) throws IOException { if (dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE && dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { return null; } final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; final Socket socket; final int soTimeoutMillis = DurationUtils.toMillisInt(dataTimeout); if (dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE) { // if no activePortRange was set (correctly) -> getActivePort() = 0 // -> new ServerSocket(0) -> bind to any free local port try (final ServerSocket server = _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress())) { // Try EPRT only if remote server is over IPv6, if not use PORT, // because EPRT has no advantage over PORT on IPv4. // It could even have the disadvantage, // that EPRT will make the data connection fail, because // today's intelligent NAT Firewalls are able to // substitute IP addresses in the PORT command, // but might not be able to recognize the EPRT command. if (isInet6Address) { if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))) { return null; } } else if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))) { return null; } if ((restartOffset > 0) && !restart(restartOffset)) { return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { return null; } // For now, let's just use the data timeout value for waiting for // the data connection. It may be desirable to let this be a // separately configurable value. In any case, we really want // to allow preventing the accept from blocking indefinitely. if (soTimeoutMillis >= 0) { server.setSoTimeout(soTimeoutMillis); } socket = server.accept(); // Ensure the timeout is set before any commands are issued on the new socket if (soTimeoutMillis >= 0) { socket.setSoTimeout(soTimeoutMillis); } if (receiveDataSocketBufferSize > 0) { socket.setReceiveBufferSize(receiveDataSocketBufferSize); } if (sendDataSocketBufferSize > 0) { socket.setSendBufferSize(sendDataSocketBufferSize); } } } else { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE // Try EPSV command first on IPv6 - and IPv4 if enabled. // When using IPv4 with NAT it has the advantage // to work with more rare configurations. // E.g. if FTP server has a static PASV address (external network) // and the client is coming from another internal network. // In that case the data connection after PASV command would fail, // while EPSV would make the client succeed by taking just the port. final boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { _parseExtendedPassiveModeReply(_replyLines.get(0)); } else { if (isInet6Address) { return null; // Must use EPSV for IPV6 } // If EPSV failed on IPV4, revert to PASV if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { return null; } _parsePassiveModeReply(_replyLines.get(0)); } socket = _socketFactory_.createSocket(); if (receiveDataSocketBufferSize > 0) { socket.setReceiveBufferSize(receiveDataSocketBufferSize); } if (sendDataSocketBufferSize > 0) { socket.setSendBufferSize(sendDataSocketBufferSize); } if (passiveLocalHost != null) { socket.bind(new InetSocketAddress(passiveLocalHost, 0)); } // For now, let's just use the data timeout value for waiting for // the data connection. It may be desirable to let this be a // separately configurable value. In any case, we really want // to allow preventing the accept from blocking indefinitely. if (soTimeoutMillis >= 0) { socket.setSoTimeout(soTimeoutMillis); } socket.connect(new InetSocketAddress(passiveHost, passivePort), connectTimeout); if ((restartOffset > 0) && !restart(restartOffset)) { socket.close(); return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { socket.close(); return null; } } if (remoteVerificationEnabled && !verifyRemote(socket)) { // Grab the host before we close the socket to avoid NET-663 final InetAddress socketHost = socket.getInetAddress(); socket.close(); throw new IOException( "Host attempting data connection " + socketHost.getHostAddress() + " is not same as server " + getRemoteAddress().getHostAddress()); } return socket; } protected void _parseExtendedPassiveModeReply(String reply) throws MalformedServerReplyException { reply = reply.substring(reply.indexOf('(') + 1, reply.indexOf(')')).trim(); final char delim1 = reply.charAt(0); final char delim2 = reply.charAt(1); final char delim3 = reply.charAt(2); final char delim4 = reply.charAt(reply.length() - 1); if ((delim1 != delim2) || (delim2 != delim3) || (delim3 != delim4)) { throw new MalformedServerReplyException("Could not parse extended passive host information.\nServer Reply: " + reply); } final int port; try { port = Integer.parseInt(reply.substring(3, reply.length() - 1)); } catch (final NumberFormatException e) { throw new MalformedServerReplyException("Could not parse extended passive host information.\nServer Reply: " + reply); } // in EPSV mode, the passive host address is implicit this.passiveHost = getRemoteAddress().getHostAddress(); this.passivePort = port; } /** * @since 3.1 * @param reply the reply to parse * @throws MalformedServerReplyException if the server reply does not match (n,n,n,n),(n),(n) */ protected void _parsePassiveModeReply(final String reply) throws MalformedServerReplyException { final Matcher m = PARMS_PAT.matcher(reply); if (!m.find()) { throw new MalformedServerReplyException("Could not parse passive host information.\nServer Reply: " + reply); } int pasvPort; // Fix up to look like IP address String pasvHost = "0,0,0,0".equals(m.group(1)) ? _socket_.getInetAddress().getHostAddress() : m.group(1).replace(',', '.'); try { final int oct1 = Integer.parseInt(m.group(2)); final int oct2 = Integer.parseInt(m.group(3)); pasvPort = (oct1 << 8) | oct2; } catch (final NumberFormatException e) { throw new MalformedServerReplyException("Could not parse passive port information.\nServer Reply: " + reply); } if (isIpAddressFromPasvResponse()) { // Pre-3.9.0 behavior if (passiveNatWorkaroundStrategy != null) { try { final String newPassiveHost = passiveNatWorkaroundStrategy.resolve(pasvHost); if (!pasvHost.equals(newPassiveHost)) { fireReplyReceived(0, "[Replacing PASV mode reply address " + this.passiveHost + " with " + newPassiveHost + "]\n"); pasvHost = newPassiveHost; } } catch (final UnknownHostException e) { // Should not happen as we are passing in an IP address throw new MalformedServerReplyException("Could not parse passive host information.\nServer Reply: " + reply); } } } else if (_socket_ == null) { pasvHost = null; // For unit testing. } else { pasvHost = _socket_.getInetAddress().getHostAddress(); } this.passiveHost = pasvHost; this.passivePort = pasvPort; } /** * @param command the command to get * @param remote the remote file name * @param local The local OutputStream to which to write the file. * @return true if successful * @throws IOException on error * @since 3.1 */ protected boolean _retrieveFile(final String command, final String remote, final OutputStream local) throws IOException { final Socket socket = _openDataConnection_(command, remote); if (socket == null) { return false; } InputStream input = null; CSL csl = null; try { try { if (fileType == ASCII_FILE_TYPE) { input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); } else { input = getBufferedInputStream(socket.getInputStream()); } if (DurationUtils.isPositive(controlKeepAliveTimeout)) { csl = new CSL(this, controlKeepAliveTimeout, controlKeepAliveReplyTimeout); } // Treat everything else as binary for now Util.copyStream(input, local, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, mergeListeners(csl), false); } finally { Util.closeQuietly(input); } // Get the transfer response return completePendingCommand(); } finally { Util.closeQuietly(socket); if (csl != null) { cslDebug = csl.cleanUp(); // fetch any outstanding keepalive replies } } } /** * @param command the command to send * @param remote the remote file name * @return the stream from which to read the file * @throws IOException on error * @since 3.1 */ protected InputStream _retrieveFileStream(final String command, final String remote) throws IOException { final Socket socket = _openDataConnection_(command, remote); if (socket == null) { return null; } final InputStream input; if (fileType == ASCII_FILE_TYPE) { // We buffer ascii transfers because the buffering has to // be interposed between FromNetASCIIOutputSream and the underlying // socket input stream. We don't buffer binary transfers // because we don't want to impose a buffering policy on the // programmer if possible. Programmers can decide on their // own if they want to wrap the SocketInputStream we return // for file types other than ASCII. input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); } else { input = socket.getInputStream(); } return new org.apache.commons.net.io.SocketInputStream(socket, input); } /** * @since 3.1 * @param command the command to send * @param remote the remote file name * @param local The local InputStream from which to read the data to be written/appended to the remote file. * @return true if successful * @throws IOException on error */ protected boolean _storeFile(final String command, final String remote, final InputStream local) throws IOException { final Socket socket = _openDataConnection_(command, remote); if (socket == null) { return false; } final OutputStream output; if (fileType == ASCII_FILE_TYPE) { output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); } else { output = getBufferedOutputStream(socket.getOutputStream()); } CSL csl = null; if (DurationUtils.isPositive(controlKeepAliveTimeout)) { csl = new CSL(this, controlKeepAliveTimeout, controlKeepAliveReplyTimeout); } // Treat everything else as binary for now try { Util.copyStream(local, output, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, mergeListeners(csl), false); output.close(); // ensure the file is fully written socket.close(); // done writing the file // Get the transfer response return completePendingCommand(); } catch (final IOException e) { Util.closeQuietly(output); // ignore close errors here Util.closeQuietly(socket); // ignore close errors here throw e; } finally { if (csl != null) { cslDebug = csl.cleanUp(); // fetch any outstanding keepalive replies } } } /** * @param command the command to send * @param remote the remote file name * @return the output stream to write to * @throws IOException on error * @since 3.1 */ protected OutputStream _storeFileStream(final String command, final String remote) throws IOException { final Socket socket = _openDataConnection_(command, remote); if (socket == null) { return null; } final OutputStream output; if (fileType == ASCII_FILE_TYPE) { // We buffer ascii transfers because the buffering has to // be interposed between ToNetASCIIOutputSream and the underlying // socket output stream. We don't buffer binary transfers // because we don't want to impose a buffering policy on the // programmer if possible. Programmers can decide on their // own if they want to wrap the SocketOutputStream we return // for file types other than ASCII. output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); } else { output = socket.getOutputStream(); } return new SocketOutputStream(socket, output); } /** * Abort a transfer in progress. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean abort() throws IOException { return FTPReply.isPositiveCompletion(abor()); } /** * Reserve a number of bytes on the server for the next file transfer. * * @param bytes The number of bytes which the server should allocate. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean allocate(final int bytes) throws IOException { return FTPReply.isPositiveCompletion(allo(bytes)); } /** * Reserve space on the server for the next file transfer. * * @param bytes The number of bytes which the server should allocate. * @param recordSize The size of a file record. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean allocate(final int bytes, final int recordSize) throws IOException { return FTPReply.isPositiveCompletion(allo(bytes, recordSize)); } /** * Reserve a number of bytes on the server for the next file transfer. * * @param bytes The number of bytes which the server should allocate. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean allocate(final long bytes) throws IOException { return FTPReply.isPositiveCompletion(allo(bytes)); } /** * Reserve space on the server for the next file transfer. * * @param bytes The number of bytes which the server should allocate. * @param recordSize The size of a file record. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean allocate(final long bytes, final int recordSize) throws IOException { return FTPReply.isPositiveCompletion(allo(bytes, recordSize)); } /** * Appends to a file on the server with the given name, taking input from the given InputStream. This method does NOT close the given InputStream. If the * current file type is ASCII, line separators in the file are transparently converted to the NETASCII format (i.e., you should not attempt to create a * special InputStream to do this). * * @param remote The name of the remote file. * @param local The local InputStream from which to read the data to be appended to the remote file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some * other reason causing the server to send FTP reply code 421. This exception may be caught either as * an IOException or independently as itself. * @throws org.apache.commons.net.io.CopyStreamException If an I/O error occurs while actually transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException causing the error. This exception may * be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the * server. */ public boolean appendFile(final String remote, final InputStream local) throws IOException { return storeFile(FTPCmd.APPE, remote, local); } /** * Returns an OutputStream through which data can be written to append to a file on the server with the given name. If the current file type is ASCII, the * returned OutputStream will convert line separators in the file to the NETASCII format (i.e., you should not attempt to create a special OutputStream to * do this). You must close the OutputStream when you finish writing to it. The OutputStream itself will take care of closing the parent data connection * socket upon being closed. *

* To finalize the file transfer you must call {@link #completePendingCommand completePendingCommand } and check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @param remote The name of the remote file. * @return An OutputStream through which the remote file can be appended. If the data connection cannot be opened (e.g., the file does not exist), null is * returned (in which case you may check the reply code to determine the exact reason for failure). * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public OutputStream appendFileStream(final String remote) throws IOException { return storeFileStream(FTPCmd.APPE, remote); } /** * Change to the parent directory of the current working directory. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean changeToParentDirectory() throws IOException { return FTPReply.isPositiveCompletion(cdup()); } /** * Change the current working directory of the FTP session. * * @param pathname The new current working directory. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean changeWorkingDirectory(final String pathname) throws IOException { return FTPReply.isPositiveCompletion(cwd(pathname)); } /** * There are a few FTPClient methods that do not complete the entire sequence of FTP commands to complete a transaction. These commands require some action * by the programmer after the reception of a positive intermediate command. After the programmer's code completes its actions, it must call this method to * receive the completion reply from the server and verify the success of the entire transaction. *

* For example, * *

     * InputStream input;
     * OutputStream output;
     * input  = new FileInputStream("foobaz.txt");
     * output = ftp.storeFileStream("foobar.txt")
     * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
     *     input.close();
     *     output.close();
     *     ftp.logout();
     *     ftp.disconnect();
     *     System.err.println("File transfer failed.");
     *     System.exit(1);
     * }
     * Util.copyStream(input, output);
     * input.close();
     * output.close();
     * // Must call completePendingCommand() to finish command.
     * if(!ftp.completePendingCommand()) {
     *     ftp.logout();
     *     ftp.disconnect();
     *     System.err.println("File transfer failed.");
     *     System.exit(1);
     * }
     * 
* * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean completePendingCommand() throws IOException { return FTPReply.isPositiveCompletion(getReply()); } /** * Implementation of the {@link Configurable Configurable} interface. In the case of this class, configuring merely makes the config object available for * the factory methods that construct parsers. * * @param config {@link FTPClientConfig FTPClientConfig} object used to provide non-standard configurations to the parser. * @since 1.4 */ @Override public void configure(final FTPClientConfig config) { this.configuration = config; } // package access for test purposes void createParser(final String parserKey) throws IOException { // We cache the value to avoid creation of a new object every // time a file listing is generated. // Note: we don't check against a null parserKey (NET-544) if (entryParser == null || (parserKey != null && !entryParserKey.equals(parserKey))) { if (null != parserKey) { // if a parser key was supplied in the parameters, // use that to create the parser entryParser = parserFactory.createFileEntryParser(parserKey); entryParserKey = parserKey; } else // if no parserKey was supplied, check for a configuration // in the params, and if it has a non-empty system type, use that. if (null != configuration && configuration.getServerSystemKey().length() > 0) { entryParser = parserFactory.createFileEntryParser(configuration); entryParserKey = configuration.getServerSystemKey(); } else { // if a parserKey hasn't been supplied, and a configuration // hasn't been supplied, and the override property is not set // then autodetect by calling // the SYST command and use that to choose the parser. String systemType = System.getProperty(FTP_SYSTEM_TYPE); if (systemType == null) { systemType = getSystemType(); // cannot be null final Properties override = getOverrideProperties(); if (override != null) { final String newType = override.getProperty(systemType); if (newType != null) { systemType = newType; } } } if (null != configuration) { // system type must have been empty above entryParser = parserFactory.createFileEntryParser(new FTPClientConfig(systemType, configuration)); } else { entryParser = parserFactory.createFileEntryParser(systemType); } entryParserKey = systemType; } } } /** * Deletes a file on the FTP server. * * @param pathname The pathname of the file to be deleted. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean deleteFile(final String pathname) throws IOException { return FTPReply.isPositiveCompletion(dele(pathname)); } /** * Closes the connection to the FTP server and restores connection parameters to the default values. * * @throws IOException If an error occurs while disconnecting. */ @Override public void disconnect() throws IOException { super.disconnect(); initDefaults(); } /** * Issue a command and wait for the reply. *

* Should only be used with commands that return replies on the command channel - do not use for LIST, NLST, MLSD etc. * * @param command The command to invoke * @param params The parameters string, may be {@code null} * @return True if successfully completed, false if not, in which case call {@link #getReplyCode()} or {@link #getReplyString()} to get the reason. * * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.0 */ public boolean doCommand(final String command, final String params) throws IOException { return FTPReply.isPositiveCompletion(sendCommand(command, params)); } /** * Issue a command and wait for the reply, returning it as an array of strings. *

* Should only be used with commands that return replies on the command channel - do not use for LIST, NLST, MLSD etc. * * @param command The command to invoke * @param params The parameters string, may be {@code null} * @return The array of replies, or {@code null} if the command failed, in which case call {@link #getReplyCode()} or {@link #getReplyString()} to get the * reason. * * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.0 */ public String[] doCommandAsStrings(final String command, final String params) throws IOException { final boolean success = FTPReply.isPositiveCompletion(sendCommand(command, params)); if (success) { return getReplyStrings(); } return null; } /** * Set the current data connection mode to ACTIVE_LOCAL_DATA_CONNECTION_MODE. No communication with the FTP server is conducted, but this * causes all future data transfers to require the FTP server to connect to the client's data port. Additionally, to accommodate differences between socket * implementations on different platforms, this method causes the client to issue a PORT command before every data transfer. */ public void enterLocalActiveMode() { dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; passiveHost = null; passivePort = -1; } /** * Set the current data connection mode to PASSIVE_LOCAL_DATA_CONNECTION_MODE . Use this method only for data transfers between the client and * server. This method causes a PASV (or EPSV) command to be issued to the server before the opening of every data connection, telling the server to open a * data port to which the client will connect to conduct data transfers. The FTPClient will stay in PASSIVE_LOCAL_DATA_CONNECTION_MODE until * the mode is changed by calling some other method such as {@link #enterLocalActiveMode enterLocalActiveMode() } *

* N.B. currently calling any connect method will reset the mode to ACTIVE_LOCAL_DATA_CONNECTION_MODE. */ public void enterLocalPassiveMode() { dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE; // These will be set when just before a data connection is opened // in _openDataConnection_() passiveHost = null; passivePort = -1; } /** * Set the current data connection mode to ACTIVE_REMOTE_DATA_CONNECTION . Use this method only for server to server data transfers. This * method issues a PORT command to the server, indicating the other server and port to which it should connect for data transfers. You must call this method * before EVERY server to server transfer attempt. The FTPClient will NOT automatically continue to issue PORT commands. You also must remember to call * {@link #enterLocalActiveMode enterLocalActiveMode() } if you wish to return to the normal data connection mode. * * @param host The passive mode server accepting connections for data transfers. * @param port The passive mode server's data port. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean enterRemoteActiveMode(final InetAddress host, final int port) throws IOException { if (FTPReply.isPositiveCompletion(port(host, port))) { dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE; passiveHost = null; passivePort = -1; return true; } return false; } /** * Set the current data connection mode to PASSIVE_REMOTE_DATA_CONNECTION_MODE . Use this method only for server to server data transfers. * This method issues a PASV command to the server, telling it to open a data port to which the active server will connect to conduct data transfers. You * must call this method before EVERY server to server transfer attempt. The FTPClient will NOT automatically continue to issue PASV commands. You also must * remember to call {@link #enterLocalActiveMode enterLocalActiveMode() } if you wish to return to the normal data connection mode. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean enterRemotePassiveMode() throws IOException { if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { return false; } dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE; _parsePassiveModeReply(_replyLines.get(0)); return true; } /** * Queries the server for supported features. The server may reply with a list of server-supported extensions. For example, a typical client-server * interaction might be (from RFC 2389): * *

        C> feat
        S> 211-Extensions supported:
        S>  MLST size*;create;modify*;perm;media-type
        S>  SIZE
        S>  COMPRESSION
        S>  MDTM
        S> 211 END
     * 
* * @see http://www.faqs.org/rfcs/rfc2389.html * @return True if successfully completed, false if not. * @throws IOException on error * @since 2.2 */ public boolean features() throws IOException { return FTPReply.isPositiveCompletion(feat()); } /** * Queries the server for a supported feature, and returns the its value (if any). Caches the parsed response to avoid resending the command repeatedly. * * @param feature the feature to check * * @return if the feature is present, returns the feature value or the empty string if the feature exists but has no value. Returns {@code null} if the * feature is not found or the command failed. Check {@link #getReplyCode()} or {@link #getReplyString()} if so. * @throws IOException on error * @since 3.0 */ public String featureValue(final String feature) throws IOException { final String[] values = featureValues(feature); if (values != null) { return values[0]; } return null; } /** * Queries the server for a supported feature, and returns its values (if any). Caches the parsed response to avoid resending the command repeatedly. * * @param feature the feature to check * * @return if the feature is present, returns the feature values (empty array if none) Returns {@code null} if the feature is not found or the command * failed. Check {@link #getReplyCode()} or {@link #getReplyString()} if so. * @throws IOException on error * @since 3.0 */ public String[] featureValues(final String feature) throws IOException { if (!initFeatureMap()) { return null; } final Set entries = featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); if (entries != null) { return entries.toArray(NetConstants.EMPTY_STRING_ARRAY); } return null; } /** * Get the client port for active mode. * * @return The client port for active mode. */ int getActivePort() { if (activeMinPort > 0 && activeMaxPort >= activeMinPort) { if (activeMaxPort == activeMinPort) { return activeMaxPort; } // Get a random port between the min and max port range return random.nextInt(activeMaxPort - activeMinPort + 1) + activeMinPort; } // default port return 0; } /** * Tells if automatic server encoding detection is enabled or disabled. * * @return true, if automatic server encoding detection is enabled. */ public boolean getAutodetectUTF8() { return autodetectEncoding; } private InputStream getBufferedInputStream(final InputStream inputStream) { if (bufferSize > 0) { return new BufferedInputStream(inputStream, bufferSize); } return new BufferedInputStream(inputStream); } private OutputStream getBufferedOutputStream(final OutputStream outputStream) { if (bufferSize > 0) { return new BufferedOutputStream(outputStream, bufferSize); } return new BufferedOutputStream(outputStream); } /** * Retrieve the current internal buffer size for buffered data streams. * * @return The current buffer size. */ public int getBufferSize() { return bufferSize; } /** * Gets how long to wait for control keep-alive message replies. * * @deprecated Use {@link #getControlKeepAliveReplyTimeoutDuration()}. * @return wait time in milliseconds. * @since 3.0 */ @Deprecated public int getControlKeepAliveReplyTimeout() { return DurationUtils.toMillisInt(controlKeepAliveReplyTimeout); } /** * Gets how long to wait for control keep-alive message replies. * * @return wait time. * @since 3.9.0 */ public Duration getControlKeepAliveReplyTimeoutDuration() { return controlKeepAliveReplyTimeout; } /** * Gets the time to wait between sending control connection keepalive messages when processing file upload or download. *

* See the class Javadoc section "Control channel keep-alive feature" *

* * @deprecated Use {@link #getControlKeepAliveTimeoutDuration()}. * @return the number of seconds between keepalive messages. * @since 3.0 */ @Deprecated public long getControlKeepAliveTimeout() { return controlKeepAliveTimeout.getSeconds(); } /** * Gets the time to wait between sending control connection keepalive messages when processing file upload or download. *

* See the class Javadoc section "Control channel keep-alive feature" *

* * @return the duration between keepalive messages. * @since 3.9.0 */ public Duration getControlKeepAliveTimeoutDuration() { return controlKeepAliveTimeout; } /** * Obtain the currently active listener. * * @return the listener, may be {@code null} * @since 3.0 */ public CopyStreamListener getCopyStreamListener() { return copyStreamListener; } /** * Get the CSL debug array. *

* For debug use only *

* Currently contains: *

    *
  • successfully acked NOOPs at end of transfer
  • *
  • unanswered NOOPs at end of transfer
  • *
  • unanswered NOOPs after fetching additional replies
  • *
  • Number of IOErrors ignored
  • *
* * @deprecated 3.7 For testing only; may be dropped or changed at any time * @return the debug array */ @Deprecated // only for use in testing public int[] getCslDebug() { return cslDebug; } /** * Returns the current data connection mode (one of the _DATA_CONNECTION_MODE constants. * * @return The current data connection mode (one of the _DATA_CONNECTION_MODE constants. */ public int getDataConnectionMode() { return dataConnectionMode; } /** * Gets the timeout to use when reading from the data connection. This timeout will be set immediately after opening the data connection, provided that the * value is ≥ 0. *

* Note: the timeout will also be applied when calling accept() whilst establishing an active local data connection. *

* * @return The default timeout used when opening a data connection socket. The value 0 means an infinite timeout. * @since 3.9.0 */ public Duration getDataTimeout() { return dataTimeout; } // Method for use by unit test code only FTPFileEntryParser getEntryParser() { return entryParser; } /** * Get the host address for active mode; allows the local address to be overridden. * * @return __activeExternalHost if non-null, else getLocalAddress() * @see #setActiveExternalIPAddress(String) */ InetAddress getHostAddress() { if (activeExternalHost != null) { return activeExternalHost; } // default local address return getLocalAddress(); } /** * @param pathname the initial pathname * @return the adjusted string with "-a" added if necessary * @since 2.0 */ protected String getListArguments(final String pathname) { if (getListHiddenFiles()) { if (pathname != null) { final StringBuilder sb = new StringBuilder(pathname.length() + 3); sb.append("-a "); sb.append(pathname); return sb.toString(); } return "-a"; } return pathname; } /** * @see #setListHiddenFiles(boolean) * @return the current state * @since 2.0 */ public boolean getListHiddenFiles() { return this.listHiddenFiles; } /** * Issue the FTP MDTM command (not supported by all servers) to retrieve the last modification time of a file. The modification string should be in the ISO * 3077 form "yyyyMMDDhhmmss(.xxx)?". The timestamp represented should also be in GMT, but not all FTP servers honor this. * * @param pathname The file path to query. * @return A string representing the last file modification time in yyyyMMDDhhmmss format. * @throws IOException if an I/O error occurs. * @since 2.0 */ public String getModificationTime(final String pathname) throws IOException { if (FTPReply.isPositiveCompletion(mdtm(pathname))) { // skip the return code (e.g. 213) and the space return getReplyString(0).substring(4); } return null; } /** * Returns the hostname or IP address (in the form of a string) returned by the server when entering passive mode. If not in passive mode, returns null. * This method only returns a valid value AFTER a data connection has been opened after a call to {@link #enterLocalPassiveMode enterLocalPassiveMode()}. * This is because FTPClient sends a PASV command to the server only just before opening a data connection, and not when you call * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. * * @return The passive host name if in passive mode, otherwise null. */ public String getPassiveHost() { return passiveHost; } /** * Set the local IP address in passive mode. Useful when there are multiple network cards. * * @return The local IP address in passive mode. */ public InetAddress getPassiveLocalIPAddress() { return this.passiveLocalHost; } /** * If in passive mode, returns the data port of the passive host. This method only returns a valid value AFTER a data connection has been opened after a * call to {@link #enterLocalPassiveMode enterLocalPassiveMode()}. This is because FTPClient sends a PASV command to the server only just before opening a * data connection, and not when you call {@link #enterLocalPassiveMode enterLocalPassiveMode()}. * * @return The data port of the passive server. If not in passive mode, undefined. */ public int getPassivePort() { return passivePort; } /** * Retrieve the value to be used for the data socket SO_RCVBUF option. * * @return The current buffer size. * @since 3.3 */ public int getReceiveDataSocketBufferSize() { return receiveDataSocketBufferSize; } /** * Get the reported host address for active mode EPRT/PORT commands; allows override of {@link #getHostAddress()}. * * Useful for FTP Client behind Firewall NAT. * * @return __reportActiveExternalHost if non-null, else getHostAddress(); */ InetAddress getReportHostAddress() { if (reportActiveExternalHost != null) { return reportActiveExternalHost; } return getHostAddress(); } /** * Fetches the restart offset. * * @return offset The offset into the remote file at which to start the next file transfer. */ public long getRestartOffset() { return restartOffset; } /** * Retrieve the value to be used for the data socket SO_SNDBUF option. * * @return The current buffer size. * @since 3.3 */ public int getSendDataSocketBufferSize() { return sendDataSocketBufferSize; } /** * Issue the FTP SIZE command to the server for a given pathname. This should produce the size of the file. * * @param pathname the file name * * @return The size information returned by the server; {@code null} if there was an error * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.7 */ public String getSize(final String pathname) throws IOException { if (FTPReply.isPositiveCompletion(size(pathname))) { return getReplyString(0).substring(4); // skip the return code (e.g. 213) and the space } return null; } /** * Issue the FTP STAT command to the server. * * @return The status information returned by the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String getStatus() throws IOException { if (FTPReply.isPositiveCompletion(stat())) { return getReplyString(); } return null; } /** * Issue the FTP STAT command to the server for a given pathname. This should produce a listing of the file or directory. * * @param pathname the file name * * @return The status information returned by the server. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String getStatus(final String pathname) throws IOException { if (FTPReply.isPositiveCompletion(stat(pathname))) { return getReplyString(); } return null; } /** * @deprecated use {@link #getSystemType()} instead * @return the name * @throws IOException on error */ @Deprecated public String getSystemName() throws IOException { if (systemName == null && FTPReply.isPositiveCompletion(syst())) { systemName = _replyLines.get(_replyLines.size() - 1).substring(4); } return systemName; } /** * Fetches the system type from the server and returns the string. This value is cached for the duration of the connection after the first call to this * method. In other words, only the first time that you invoke this method will it issue a SYST command to the FTP server. FTPClient will remember the value * and return the cached value until a call to disconnect. *

* If the SYST command fails, and the system property {@link #FTP_SYSTEM_TYPE_DEFAULT} is defined, then this is used instead. * * @return The system type obtained from the server. Never null. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server (and the * default system type property is not defined) * @since 2.2 */ public String getSystemType() throws IOException { // if (syst() == FTPReply.NAME_SYSTEM_TYPE) // Technically, we should expect a NAME_SYSTEM_TYPE response, but // in practice FTP servers deviate, so we soften the condition to // a positive completion. if (systemName == null) { if (FTPReply.isPositiveCompletion(syst())) { // Assume that response is not empty here (cannot be null) systemName = _replyLines.get(_replyLines.size() - 1).substring(4); } else { // Check if the user has provided a default for when the SYST command fails final String systDefault = System.getProperty(FTP_SYSTEM_TYPE_DEFAULT); if (systDefault == null) { throw new IOException("Unable to determine system type - response: " + getReplyString()); } systemName = systDefault; } } return systemName; } /** * Queries the server for a supported feature. Caches the parsed response to avoid resending the command repeatedly. * * @param feature the name of the feature; it is converted to upper case. * @return {@code true} if the feature is present, {@code false} if the feature is not present or the {@link #feat()} command failed. Check * {@link #getReplyCode()} or {@link #getReplyString()} if it is necessary to distinguish these cases. * * @throws IOException on error * @since 3.8.0 */ public boolean hasFeature(final FTPCmd feature) throws IOException { return hasFeature(feature.name()); } /** * Queries the server for a supported feature. Caches the parsed response to avoid resending the command repeatedly. * * @param feature the name of the feature; it is converted to upper case. * @return {@code true} if the feature is present, {@code false} if the feature is not present or the {@link #feat()} command failed. Check * {@link #getReplyCode()} or {@link #getReplyString()} if it is necessary to distinguish these cases. * * @throws IOException on error * @since 3.0 */ public boolean hasFeature(final String feature) throws IOException { if (!initFeatureMap()) { return false; } return featuresMap.containsKey(feature.toUpperCase(Locale.ENGLISH)); } /** * Queries the server for a supported feature with particular value, for example "AUTH SSL" or "AUTH TLS". Caches the parsed response to avoid resending the * command repeatedly. * * @param feature the name of the feature; it is converted to upper case. * @param value the value to find. * * @return {@code true} if the feature is present, {@code false} if the feature is not present or the {@link #feat()} command failed. Check * {@link #getReplyCode()} or {@link #getReplyString()} if it is necessary to distinguish these cases. * * @throws IOException on error * @since 3.0 */ public boolean hasFeature(final String feature, final String value) throws IOException { if (!initFeatureMap()) { return false; } final Set entries = featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); if (entries != null) { return entries.contains(value); } return false; } private void initDefaults() { dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; passiveHost = null; passivePort = -1; activeExternalHost = null; reportActiveExternalHost = null; activeMinPort = 0; activeMaxPort = 0; fileType = FTP.ASCII_FILE_TYPE; fileStructure = FTP.FILE_STRUCTURE; fileFormat = FTP.NON_PRINT_TEXT_FORMAT; fileTransferMode = FTP.STREAM_TRANSFER_MODE; restartOffset = 0; systemName = null; entryParser = null; entryParserKey = ""; featuresMap = null; } /* * Create the feature map if not already created. */ private boolean initFeatureMap() throws IOException { if (featuresMap == null) { // Don't create map here, because next line may throw exception final int replyCode = feat(); if (replyCode == FTPReply.NOT_LOGGED_IN) { // 503 return false; // NET-518; don't create empy map } final boolean success = FTPReply.isPositiveCompletion(replyCode); // we init the map here, so we don't keep trying if we know the command will fail featuresMap = new HashMap<>(); if (!success) { return false; } for (final String line : _replyLines) { if (line.startsWith(" ")) { // it's a FEAT entry String key; String value = ""; final int varsep = line.indexOf(' ', 1); if (varsep > 0) { key = line.substring(1, varsep); value = line.substring(varsep + 1); } else { key = line.substring(1); } key = key.toUpperCase(Locale.ENGLISH); final Set entries = featuresMap.computeIfAbsent(key, k -> new HashSet<>()); entries.add(value); } } } return true; } /** * Using the default autodetect mechanism, initialize an FTPListParseEngine object containing a raw file information for the current working directory on * the server This information is obtained through the LIST command. This object is then capable of being iterated to return a sequence of FTPFile objects * with information filled in by the FTPFileEntryParser used. *

* This method differs from using the listFiles() methods in that expensive FTPFile objects are not created until needed which may be an advantage on large * lists. * * @return A FTPListParseEngine object that holds the raw information and is capable of providing parsed FTPFile objects, one for each file containing * information contained in the given path in the format determined by the parser parameter. Null will be returned if a data * connection cannot be opened. If the current working directory contains no files, an empty array will be the return. * * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client * being idle or some other reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving * a reply from the server. * @throws org.apache.commons.net.ftp.parser.ParserInitializationException Thrown if the autodetect mechanism cannot resolve the type of system we are * connected with. * @see FTPListParseEngine */ public FTPListParseEngine initiateListParsing() throws IOException { return initiateListParsing((String) null); } /** * private method through which all listFiles() and initiateListParsing methods pass once a parser is determined. * * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @see FTPListParseEngine */ private FTPListParseEngine initiateListParsing(final FTPFileEntryParser parser, final String pathname) throws IOException { final Socket socket = _openDataConnection_(FTPCmd.LIST, getListArguments(pathname)); final FTPListParseEngine engine = new FTPListParseEngine(parser, configuration); if (socket == null) { return engine; } try { engine.readServerList(socket.getInputStream(), getControlEncoding()); } finally { Util.closeQuietly(socket); } completePendingCommand(); return engine; } /** * Using the default autodetect mechanism, initialize an FTPListParseEngine object containing a raw file information for the supplied directory. This * information is obtained through the LIST command. This object is then capable of being iterated to return a sequence of FTPFile objects with information * filled in by the FTPFileEntryParser used. *

* The server may or may not expand glob expressions. You should avoid using glob expressions because the return format for glob listings differs from * server to server and will likely cause this method to fail. *

* This method differs from using the listFiles() methods in that expensive FTPFile objects are not created until needed which may be an advantage on large * lists. * *

     * FTPClient f = FTPClient();
     * f.connect(server);
     * f.login(username, password);
     * FTPListParseEngine engine = f.initiateListParsing(directory);
     *
     * while (engine.hasNext()) {
     *     FTPFile[] files = engine.getNext(25); // "page size" you want
     *     // do whatever you want with these files, display them, etc.
     *     // expensive FTPFile objects not created until needed.
     * }
     * 
* * @param pathname the starting directory * * @return A FTPListParseEngine object that holds the raw information and is capable of providing parsed FTPFile objects, one for each file containing * information contained in the given path in the format determined by the parser parameter. Null will be returned if a data * connection cannot be opened. If the current working directory contains no files, an empty array will be the return. * * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client * being idle or some other reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving * a reply from the server. * @throws org.apache.commons.net.ftp.parser.ParserInitializationException Thrown if the autodetect mechanism cannot resolve the type of system we are * connected with. * @see FTPListParseEngine */ public FTPListParseEngine initiateListParsing(final String pathname) throws IOException { return initiateListParsing((String) null, pathname); } /** * Using the supplied parser key, initialize an FTPListParseEngine object containing a raw file information for the supplied directory. This information is * obtained through the LIST command. This object is then capable of being iterated to return a sequence of FTPFile objects with information filled in by * the FTPFileEntryParser used. *

* The server may or may not expand glob expressions. You should avoid using glob expressions because the return format for glob listings differs from * server to server and will likely cause this method to fail. *

* This method differs from using the listFiles() methods in that expensive FTPFile objects are not created until needed which may be an advantage on large * lists. * * @param parserKey A string representing a designated code or fully-qualified class name of an FTPFileEntryParser that should be used to * parse each server file listing. May be {@code null}, in which case the code checks first the system property {@link #FTP_SYSTEM_TYPE}, * and if that is not defined the SYST command is used to provide the value. To allow for arbitrary system types, the return from the SYST * command is used to look up an alias for the type in the {@link #SYSTEM_TYPE_PROPERTIES} properties file if it is available. * @param pathname the starting directory * * @return A FTPListParseEngine object that holds the raw information and is capable of providing parsed FTPFile objects, one for each file containing * information contained in the given path in the format determined by the parser parameter. Null will be returned if a data * connection cannot be opened. If the current working directory contains no files, an empty array will be the return. * * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client * being idle or some other reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving * a reply from the server. * @throws org.apache.commons.net.ftp.parser.ParserInitializationException Thrown if the parserKey parameter cannot be resolved by the selected parser * factory. In the DefaultFTPEntryParserFactory, this will happen when parserKey is * neither the fully qualified class name of a class implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser nor a string containing one of the * recognized keys mapping to such a parser or if class loader security issues * prevent its being loaded. * @see FTPListParseEngine */ public FTPListParseEngine initiateListParsing(final String parserKey, final String pathname) throws IOException { createParser(parserKey); // create and cache parser return initiateListParsing(entryParser, pathname); } /** * Initiate list parsing for MLSD listings in the current working directory. * * @return the engine * @throws IOException on error */ public FTPListParseEngine initiateMListParsing() throws IOException { return initiateMListParsing(null); } /** * Initiate list parsing for MLSD listings. * * @param pathname the path from where to MLSD. * @return the engine. * @throws IOException on error */ public FTPListParseEngine initiateMListParsing(final String pathname) throws IOException { final Socket socket = _openDataConnection_(FTPCmd.MLSD, pathname); final FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance(), configuration); if (socket == null) { return engine; } try { engine.readServerList(socket.getInputStream(), getControlEncoding()); } finally { Util.closeQuietly(socket); completePendingCommand(); } return engine; } /** * Returns, whether the IP address from the server's response should be used. Until 3.9.0, this has always been the case. Beginning with 3.9.0, that IP * address will be silently ignored, and replaced with the remote IP address of the control connection, unless this configuration option is given, which * restores the old behavior. To enable this by default, use the system property {@link FTPClient#FTP_IP_ADDRESS_FROM_PASV_RESPONSE}. * * @return True, if the IP address from the server's response will be used (pre-3.9 compatible behavior), or false (ignore that IP address). * * @see FTPClient#FTP_IP_ADDRESS_FROM_PASV_RESPONSE * @see #setIpAddressFromPasvResponse(boolean) * @since 3.9.0 */ public boolean isIpAddressFromPasvResponse() { return ipAddressFromPasvResponse; } /** * Return whether or not verification of the remote host participating in data connections is enabled. The default behavior is for verification to be * enabled. * * @return True if verification is enabled, false if not. */ public boolean isRemoteVerificationEnabled() { return remoteVerificationEnabled; } /** * Whether should attempt to use EPSV with IPv4. Default (if not set) is false * * @return true if should attempt EPSV * @since 2.2 */ public boolean isUseEPSVwithIPv4() { return useEPSVwithIPv4; } /** * Using the default system autodetect mechanism, obtain a list of directories contained in the current working directory. *

* This information is obtained through the LIST command. The contents of the returned array is determined by the FTPFileEntryParser used. *

* N.B. the LIST command does not generally return very precise timestamps. For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. If the server supports it, the MLSD command returns timestamps with a precision of seconds, and may * include milliseconds. See {@link #mlistDir()} * * @return The list of directories contained in the current directory in the format determined by the autodetection mechanism. * * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client * being idle or some other reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving * a reply from the server. * @throws org.apache.commons.net.ftp.parser.ParserInitializationException Thrown if the parserKey parameter cannot be resolved by the selected parser * factory. In the DefaultFTPEntryParserFactory, this will happen when parserKey is * neither the fully qualified class name of a class implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser nor a string containing one of the * recognized keys mapping to such a parser or if class loader security issues * prevent its being loaded. * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory * @see org.apache.commons.net.ftp.FTPFileEntryParser * @since 3.0 */ public FTPFile[] listDirectories() throws IOException { return listDirectories((String) null); } /** * Using the default system autodetect mechanism, obtain a list of directories contained in the specified directory. *

* This information is obtained through the LIST command. The contents of the returned array is determined by the FTPFileEntryParser used. *

* N.B. the LIST command does not generally return very precise timestamps. For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. If the server supports it, the MLSD command returns timestamps with a precision of seconds, and may * include milliseconds. See {@link #mlistDir()} * * @param parent the starting directory * * @return The list of directories contained in the specified directory in the format determined by the autodetection mechanism. * * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client * being idle or some other reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving * a reply from the server. * @throws org.apache.commons.net.ftp.parser.ParserInitializationException Thrown if the parserKey parameter cannot be resolved by the selected parser * factory. In the DefaultFTPEntryParserFactory, this will happen when parserKey is * neither the fully qualified class name of a class implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser nor a string containing one of the * recognized keys mapping to such a parser or if class loader security issues * prevent its being loaded. * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory * @see org.apache.commons.net.ftp.FTPFileEntryParser * @since 3.0 */ public FTPFile[] listDirectories(final String parent) throws IOException { return listFiles(parent, FTPFileFilters.DIRECTORIES); } /** * Using the default system autodetect mechanism, obtain a list of file information for the current working directory. *

* This information is obtained through the LIST command. The contents of the returned array is determined by the FTPFileEntryParser used. *

* N.B. the LIST command does not generally return very precise timestamps. For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. If the server supports it, the MLSD command returns timestamps with a precision of seconds, and may * include milliseconds. See {@link #mlistDir()} * * @return The list of file information contained in the current directory in the format determined by the autodetection mechanism. *

* NOTE: This array may contain null members if any of the individual file listings failed to parse. The caller should check each entry for * null before referencing it. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client * being idle or some other reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving * a reply from the server. * @throws org.apache.commons.net.ftp.parser.ParserInitializationException Thrown if the parserKey parameter cannot be resolved by the selected parser * factory. In the DefaultFTPEntryParserFactory, this will happen when parserKey is * neither the fully qualified class name of a class implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser nor a string containing one of the * recognized keys mapping to such a parser or if class loader security issues * prevent its being loaded. * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory * @see org.apache.commons.net.ftp.FTPFileEntryParser */ public FTPFile[] listFiles() throws IOException { return listFiles((String) null); } /** * Using the default system autodetect mechanism, obtain a list of file information for the current working directory or for just a single file. *

* This information is obtained through the LIST command. The contents of the returned array is determined by the FTPFileEntryParser used. *

* N.B. the LIST command does not generally return very precise timestamps. For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. If the server supports it, the MLSD command returns timestamps with a precision of seconds, and may * include milliseconds. See {@link #mlistDir()} * * @param pathname The file or directory to list. Since the server may or may not expand glob expressions, using them here is not recommended and may well * cause this method to fail. Also, some servers treat a leading '-' as being an option. To avoid this interpretation, use an absolute * pathname or prefix the pathname with ./ (unix style servers). Some servers may support "--" as meaning end of options, in which case "-- * -xyz" should work. * * @return The list of file information contained in the given path in the format determined by the autodetection mechanism * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client * being idle or some other reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving * a reply from the server. * @throws org.apache.commons.net.ftp.parser.ParserInitializationException Thrown if the parserKey parameter cannot be resolved by the selected parser * factory. In the DefaultFTPEntryParserFactory, this will happen when parserKey is * neither the fully qualified class name of a class implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser nor a string containing one of the * recognized keys mapping to such a parser or if class loader security issues * prevent its being loaded. * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory * @see org.apache.commons.net.ftp.FTPFileEntryParser */ public FTPFile[] listFiles(final String pathname) throws IOException { return initiateListParsing((String) null, pathname).getFiles(); } /** * Version of {@link #listFiles(String)} which allows a filter to be provided. For example: listFiles("site", FTPFileFilters.DIRECTORY); * * @param pathname the initial path, may be null * @param filter the filter, non-null * @return the list of FTPFile entries. * @throws IOException on error * @since 2.2 */ public FTPFile[] listFiles(final String pathname, final FTPFileFilter filter) throws IOException { return initiateListParsing((String) null, pathname).getFiles(filter); } /** * Fetches the system help information from the server and returns the full string. * * @return The system help string obtained from the server. null if the information could not be obtained. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String listHelp() throws IOException { return FTPReply.isPositiveCompletion(help()) ? getReplyString() : null; } /** * Fetches the help information for a given command from the server and returns the full string. * * @param command The command on which to ask for help. * @return The command help string obtained from the server. null if the information could not be obtained. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String listHelp(final String command) throws IOException { return FTPReply.isPositiveCompletion(help(command)) ? getReplyString() : null; } /** * Obtain a list of file names in the current working directory This information is obtained through the NLST command. If the current directory contains no * files, a zero length array is returned only if the FTP server returned a positive completion code, otherwise, null is returned (the FTP server returned a * 550 error No files found.). If the directory is not empty, an array of file names in the directory is returned. * * @return The list of file names contained in the current working directory. null if the list could not be obtained. If there are no file names in the * directory, a zero-length array is returned. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String[] listNames() throws IOException { return listNames(null); } /** * Obtain a list of file names in a directory (or just the name of a given file, which is not particularly useful). This information is obtained through the * NLST command. If the given pathname is a directory and contains no files, a zero length array is returned only if the FTP server returned a positive * completion code, otherwise null is returned (the FTP server returned a 550 error No files found.). If the directory is not empty, an array of file names * in the directory is returned. If the pathname corresponds to a file, only that file will be listed. The server may or may not expand glob expressions. * * @param pathname The file or directory to list. Warning: the server may treat a leading '-' as an option introducer. If so, try using an absolute path, or * prefix the path with ./ (unix style servers). Some servers may support "--" as meaning end of options, in which case "-- -xyz" should * work. * @return The list of file names contained in the given path. null if the list could not be obtained. If there are no file names in the directory, a * zero-length array is returned. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String[] listNames(final String pathname) throws IOException { final ArrayList results = new ArrayList<>(); try (final Socket socket = _openDataConnection_(FTPCmd.NLST, getListArguments(pathname))) { if (socket == null) { return null; } try (final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding()))) { String line; while ((line = reader.readLine()) != null) { results.add(line); } } } if (completePendingCommand()) { return results.toArray(NetConstants.EMPTY_STRING_ARRAY); } return null; } /** * Login to the FTP server using the provided username and password. * * @param username The username to login under. * @param password The password to use. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean login(final String username, final String password) throws IOException { user(username); if (FTPReply.isPositiveCompletion(_replyCode)) { return true; } // If we get here, we either have an error code, or an intermmediate // reply requesting password. if (!FTPReply.isPositiveIntermediate(_replyCode)) { return false; } return FTPReply.isPositiveCompletion(pass(password)); } /** * Login to the FTP server using the provided username, password, and account. If no account is required by the server, only the username and password, the * account information is not used. * * @param username The username to login under. * @param password The password to use. * @param account The account to use. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean login(final String username, final String password, final String account) throws IOException { user(username); if (FTPReply.isPositiveCompletion(_replyCode)) { return true; } // If we get here, we either have an error code, or an intermmediate // reply requesting password. if (!FTPReply.isPositiveIntermediate(_replyCode)) { return false; } pass(password); if (FTPReply.isPositiveCompletion(_replyCode)) { return true; } if (!FTPReply.isPositiveIntermediate(_replyCode)) { return false; } return FTPReply.isPositiveCompletion(acct(account)); } /** * Logout of the FTP server by sending the QUIT command. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean logout() throws IOException { return FTPReply.isPositiveCompletion(quit()); } /** * Creates a new subdirectory on the FTP server in the current directory (if a relative pathname is given) or where specified (if an absolute pathname is * given). * * @param pathname The pathname of the directory to create. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean makeDirectory(final String pathname) throws IOException { return FTPReply.isPositiveCompletion(mkd(pathname)); } /** * Issue the FTP MDTM command (not supported by all servers) to retrieve the last modification time of a file. The modification string should be in the ISO * 3077 form "yyyyMMDDhhmmss(.xxx)?". The timestamp represented should also be in GMT, but not all FTP servers honor this. * * @param pathname The file path to query. * @return A Calendar representing the last file modification time, may be {@code null}. The Calendar timestamp will be null if a parse error occurs. * @throws IOException if an I/O error occurs. * @since 3.8.0 */ public Calendar mdtmCalendar(final String pathname) throws IOException { final String modificationTime = getModificationTime(pathname); if (modificationTime != null) { return MLSxEntryParser.parseGMTdateTime(modificationTime); } return null; } /** * Issue the FTP MDTM command (not supported by all servers) to retrieve the last modification time of a file. The modification string should be in the ISO * 3077 form "yyyyMMDDhhmmss(.xxx)?". The timestamp represented should also be in GMT, but not all FTP servers honor this. * * @param pathname The file path to query. * @return A FTPFile representing the last file modification time, may be {@code null}. The FTPFile timestamp will be null if a parse error occurs. * @throws IOException if an I/O error occurs. * @since 3.4 */ public FTPFile mdtmFile(final String pathname) throws IOException { final String modificationTime = getModificationTime(pathname); if (modificationTime != null) { final FTPFile file = new FTPFile(); file.setName(pathname); file.setRawListing(modificationTime); file.setTimestamp(MLSxEntryParser.parseGMTdateTime(modificationTime)); return file; } return null; } /** * Issue the FTP MDTM command (not supported by all servers) to retrieve the last modification time of a file. The modification string should be in the ISO * 3077 form "yyyyMMDDhhmmss(.xxx)?". The timestamp represented should also be in GMT, but not all FTP servers honor this. * * @param pathname The file path to query. * @return An Instant representing the last file modification time, may be {@code null}. The Instant timestamp will be null if a parse error occurs. * @throws IOException if an I/O error occurs. * @since 3.9.0 */ public Instant mdtmInstant(final String pathname) throws IOException { final String modificationTime = getModificationTime(pathname); if (modificationTime != null) { return MLSxEntryParser.parseGmtInstant(modificationTime); } return null; } /** * Merge two copystream listeners, either or both of which may be null. * * @param local the listener used by this class, may be null * @return a merged listener or a single listener or null * @since 3.0 */ private CopyStreamListener mergeListeners(final CopyStreamListener local) { if (local == null) { return copyStreamListener; } if (copyStreamListener == null) { return local; } // Both are non-null final CopyStreamAdapter merged = new CopyStreamAdapter(); merged.addCopyStreamListener(local); merged.addCopyStreamListener(copyStreamListener); return merged; } /** * Generate a directory listing for the current directory using the MLSD command. * * @return the array of file entries * @throws IOException on error * @since 3.0 */ public FTPFile[] mlistDir() throws IOException { return mlistDir(null); } /** * Generate a directory listing using the MLSD command. * * @param pathname the directory name, may be {@code null} * @return the array of file entries * @throws IOException on error * @since 3.0 */ public FTPFile[] mlistDir(final String pathname) throws IOException { return initiateMListParsing(pathname).getFiles(); } /** * Generate a directory listing using the MLSD command. * * @param pathname the directory name, may be {@code null} * @param filter the filter to apply to the responses * @return the array of file entries * @throws IOException on error * @since 3.0 */ public FTPFile[] mlistDir(final String pathname, final FTPFileFilter filter) throws IOException { return initiateMListParsing(pathname).getFiles(filter); } /** * Get file details using the MLST command * * @param pathname the file or directory to list, may be {@code null} * @return the file details, may be {@code null} * @throws IOException on error * @since 3.0 */ public FTPFile mlistFile(final String pathname) throws IOException { final boolean success = FTPReply.isPositiveCompletion(sendCommand(FTPCmd.MLST, pathname)); if (success) { String reply = getReplyString(1); // some FTP server reply not contains space before fact(s) if (reply.charAt(0) != ' ') { reply = " " + reply; } /* * check the response makes sense. Must have space before fact(s) and between fact(s) and file name Fact(s) can be absent, so at least 3 chars are * needed. */ if (reply.length() < 3) { throw new MalformedServerReplyException("Invalid server reply (MLST): '" + reply + "'"); } // some FTP server reply contains more than one space before fact(s) final String entry = reply.replaceAll("^\\s+", ""); // skip leading space for parser return MLSxEntryParser.parseEntry(entry); } return null; } /** * Returns the pathname of the current working directory. * * @return The pathname of the current working directory. If it cannot be obtained, returns null. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String printWorkingDirectory() throws IOException { if (pwd() != FTPReply.PATHNAME_CREATED) { return null; } return parsePathname(_replyLines.get(_replyLines.size() - 1)); } /** * Reinitialize the FTP session. Not all FTP servers support this command, which issues the FTP REIN command. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.4 (made public) */ public boolean reinitialize() throws IOException { rein(); if (FTPReply.isPositiveCompletion(_replyCode) || (FTPReply.isPositivePreliminary(_replyCode) && FTPReply.isPositiveCompletion(getReply()))) { initDefaults(); return true; } return false; } // For server to server transfers /** * Initiate a server to server file transfer. This method tells the server to which the client is connected to append to a given file on the other server. * The other server must have had a remoteRetrieve issued to it by another FTPClient. * * @param fileName The name of the file to be appended to, or if the file does not exist, the name to call the file being stored. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean remoteAppend(final String fileName) throws IOException { if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { return FTPReply.isPositivePreliminary(appe(fileName)); } return false; } /** * Initiate a server to server file transfer. This method tells the server to which the client is connected to retrieve a given file from the other server. * * @param fileName The name of the file to retrieve. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean remoteRetrieve(final String fileName) throws IOException { if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { return FTPReply.isPositivePreliminary(retr(fileName)); } return false; } /** * Initiate a server to server file transfer. This method tells the server to which the client is connected to store a file on the other server using the * given file name. The other server must have had a remoteRetrieve issued to it by another FTPClient. * * @param fileName The name to call the file that is to be stored. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean remoteStore(final String fileName) throws IOException { if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { return FTPReply.isPositivePreliminary(stor(fileName)); } return false; } /** * Initiate a server to server file transfer. This method tells the server to which the client is connected to store a file on the other server using a * unique file name. The other server must have had a remoteRetrieve issued to it by another FTPClient. Many FTP servers require that a base * file name be given from which the unique file name can be derived. For those servers use the other version of remoteStoreUnique * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean remoteStoreUnique() throws IOException { if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { return FTPReply.isPositivePreliminary(stou()); } return false; } /** * Initiate a server to server file transfer. This method tells the server to which the client is connected to store a file on the other server using a * unique file name based on the given file name. The other server must have had a remoteRetrieve issued to it by another FTPClient. * * @param fileName The name on which to base the file name of the file that is to be stored. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean remoteStoreUnique(final String fileName) throws IOException { if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { return FTPReply.isPositivePreliminary(stou(fileName)); } return false; } /** * Removes a directory on the FTP server (if empty). * * @param pathname The pathname of the directory to remove. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean removeDirectory(final String pathname) throws IOException { return FTPReply.isPositiveCompletion(rmd(pathname)); } /** * Renames a remote file. * * @param from The name of the remote file to rename. * @param to The new name of the remote file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean rename(final String from, final String to) throws IOException { if (!FTPReply.isPositiveIntermediate(rnfr(from))) { return false; } return FTPReply.isPositiveCompletion(rnto(to)); } /** * Restart a STREAM_TRANSFER_MODE file transfer starting from the given offset. This will only work on FTP servers supporting the REST comand * for the stream transfer mode. However, most FTP servers support this. Any subsequent file transfer will start reading or writing the remote file from the * indicated offset. * * @param offset The offset into the remote file at which to start the next file transfer. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.1 (changed from private to protected) */ protected boolean restart(final long offset) throws IOException { restartOffset = 0; return FTPReply.isPositiveIntermediate(rest(Long.toString(offset))); } /** * Retrieves a named file from the server and writes it to the given OutputStream. This method does NOT close the given OutputStream. If the current file * type is ASCII, line separators in the file are converted to the local representation. *

* Note: if you have used {@link #setRestartOffset(long)}, the file data will start from the selected offset. * * @param remote The name of the remote file. * @param local The local OutputStream to which to write the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some * other reason causing the server to send FTP reply code 421. This exception may be caught either as * an IOException or independently as itself. * @throws org.apache.commons.net.io.CopyStreamException If an I/O error occurs while actually transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException causing the error. This exception may * be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the * server. */ public boolean retrieveFile(final String remote, final OutputStream local) throws IOException { return _retrieveFile(FTPCmd.RETR.getCommand(), remote, local); } /** * Returns an InputStream from which a named file from the server can be read. If the current file type is ASCII, the returned InputStream will convert line * separators in the file to the local representation. You must close the InputStream when you finish reading from it. The InputStream itself will take care * of closing the parent data connection socket upon being closed. *

* To finalize the file transfer you must call {@link #completePendingCommand completePendingCommand } and check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. *

* Note: if you have used {@link #setRestartOffset(long)}, the file data will start from the selected offset. * * @param remote The name of the remote file. * @return An InputStream from which the remote file can be read. If the data connection cannot be opened (e.g., the file does not exist), null is returned * (in which case you may check the reply code to determine the exact reason for failure). * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public InputStream retrieveFileStream(final String remote) throws IOException { return _retrieveFileStream(FTPCmd.RETR.getCommand(), remote); } /** * Sends a NOOP command to the FTP server. This is useful for preventing server timeouts. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean sendNoOp() throws IOException { return FTPReply.isPositiveCompletion(noop()); } /** * Send a site specific command. * * @param arguments The site specific command and arguments. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean sendSiteCommand(final String arguments) throws IOException { return FTPReply.isPositiveCompletion(site(arguments)); } /** * Set the external IP address in active mode. Useful when there are multiple network cards. * * @param ipAddress The external IP address of this machine. * @throws UnknownHostException if the ipAddress cannot be resolved * @since 2.2 */ public void setActiveExternalIPAddress(final String ipAddress) throws UnknownHostException { this.activeExternalHost = InetAddress.getByName(ipAddress); } /** * Set the client side port range in active mode. * * @param minPort The lowest available port (inclusive). * @param maxPort The highest available port (inclusive). * @since 2.2 */ public void setActivePortRange(final int minPort, final int maxPort) { this.activeMinPort = minPort; this.activeMaxPort = maxPort; } /** * Enables or disables automatic server encoding detection (only UTF-8 supported). *

* Does not affect existing connections; must be invoked before a connection is established. * * @param autodetect If true, automatic server encoding detection will be enabled. */ public void setAutodetectUTF8(final boolean autodetect) { autodetectEncoding = autodetect; } /** * Set the internal buffer size for buffered data streams. * * @param bufSize The size of the buffer. Use a non-positive value to use the default. */ public void setBufferSize(final int bufSize) { bufferSize = bufSize; } /** * Sets how long to wait for control keep-alive message replies. * * @param timeout number of milliseconds to wait (defaults to 1000) * @since 3.0 * @see #setControlKeepAliveTimeout(Duration) */ public void setControlKeepAliveReplyTimeout(final Duration timeout) { controlKeepAliveReplyTimeout = DurationUtils.zeroIfNull(timeout); } /** * Sets how long to wait for control keep-alive message replies. * * @deprecated Use {@link #setControlKeepAliveReplyTimeout(Duration)}. * @param timeoutMillis number of milliseconds to wait (defaults to 1000) * @since 3.0 * @see #setControlKeepAliveTimeout(long) */ @Deprecated public void setControlKeepAliveReplyTimeout(final int timeoutMillis) { controlKeepAliveReplyTimeout = Duration.ofMillis(timeoutMillis); } /** * Sets the time to wait between sending control connection keepalive messages when processing file upload or download. *

* See the class Javadoc section "Control channel keep-alive feature" *

* * @param controlIdle the wait between keepalive messages. Zero (or less) disables. * @since 3.9.0 * @see #setControlKeepAliveReplyTimeout(Duration) */ public void setControlKeepAliveTimeout(final Duration controlIdle) { controlKeepAliveTimeout = DurationUtils.zeroIfNull(controlIdle); } /** * Sets the time to wait between sending control connection keepalive messages when processing file upload or download. *

* See the class Javadoc section "Control channel keep-alive feature" *

* * @deprecated Use {@link #setControlKeepAliveTimeout(Duration)}. * @param controlIdleSeconds the wait (in seconds) between keepalive messages. Zero (or less) disables. * @since 3.0 * @see #setControlKeepAliveReplyTimeout(int) */ @Deprecated public void setControlKeepAliveTimeout(final long controlIdleSeconds) { controlKeepAliveTimeout = Duration.ofSeconds(controlIdleSeconds); } /** * Set the listener to be used when performing store/retrieve operations. The default value (if not set) is {@code null}. * * @param listener to be used, may be {@code null} to disable * @since 3.0 */ public void setCopyStreamListener(final CopyStreamListener listener) { copyStreamListener = listener; } /** * Sets the timeout to use when reading from the data connection. This timeout will be set immediately after opening the data connection, provided that the * value is ≥ 0. *

* Note: the timeout will also be applied when calling accept() whilst establishing an active local data connection. * * @param timeout The default timeout that is used when opening a data connection socket. The value 0 (or null) means an infinite timeout. * @since 3.9.0 */ public void setDataTimeout(final Duration timeout) { dataTimeout = DurationUtils.zeroIfNull(timeout); } /** * Sets the timeout in milliseconds to use when reading from the data connection. This timeout will be set immediately after opening the data connection, * provided that the value is ≥ 0. *

* Note: the timeout will also be applied when calling accept() whilst establishing an active local data connection. *

* * @deprecated Use {@link #setDataTimeout(Duration)}. * @param timeoutMillis The default timeout in milliseconds that is used when opening a data connection socket. The value 0 means an infinite timeout. */ @Deprecated public void setDataTimeout(final int timeoutMillis) { dataTimeout = Duration.ofMillis(timeoutMillis); } /** * Sets the file structure. The default structure is FTP.FILE_STRUCTURE if this method is never called or if a connect method is called. * * @param structure The structure of the file (one of the FTP class _STRUCTURE constants). * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean setFileStructure(final int structure) throws IOException { if (FTPReply.isPositiveCompletion(stru(structure))) { fileStructure = structure; return true; } return false; } /** * Sets the transfer mode. The default transfer mode FTP.STREAM_TRANSFER_MODE if this method is never called or if a connect method is * called. * * @param mode The new transfer mode to use (one of the FTP class _TRANSFER_MODE constants). * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean setFileTransferMode(final int mode) throws IOException { if (FTPReply.isPositiveCompletion(mode(mode))) { fileTransferMode = mode; return true; } return false; } /** * Sets the file type to be transferred. This should be one of FTP.ASCII_FILE_TYPE , FTP.BINARY_FILE_TYPE, etc. The file type * only needs to be set when you want to change the type. After changing it, the new type stays in effect until you change it again. The default file type * is FTP.ASCII_FILE_TYPE if this method is never called.
* The server default is supposed to be ASCII (see RFC 959), however many ftp servers default to BINARY. To ensure correct operation with all servers, * always specify the appropriate file type after connecting to the server.
*

* N.B. currently calling any connect method will reset the type to FTP.ASCII_FILE_TYPE. * * @param fileType The _FILE_TYPE constant indicating the type of file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean setFileType(final int fileType) throws IOException { if (FTPReply.isPositiveCompletion(type(fileType))) { this.fileType = fileType; this.fileFormat = FTP.NON_PRINT_TEXT_FORMAT; return true; } return false; } /** * Sets the file type to be transferred and the format. The type should be one of FTP.ASCII_FILE_TYPE , FTP.BINARY_FILE_TYPE , * etc. The file type only needs to be set when you want to change the type. After changing it, the new type stays in effect until you change it again. The * default file type is FTP.ASCII_FILE_TYPE if this method is never called.
* The server default is supposed to be ASCII (see RFC 959), however many ftp servers default to BINARY. To ensure correct operation with all servers, * always specify the appropriate file type after connecting to the server.
* The format should be one of the FTP class TEXT_FORMAT constants, or if the type is FTP.LOCAL_FILE_TYPE , the format should * be the byte size for that type. The default format is FTP.NON_PRINT_TEXT_FORMAT if this method is never called. *

* N.B. currently calling any connect method will reset the type to FTP.ASCII_FILE_TYPE and the formatOrByteSize to FTP.NON_PRINT_TEXT_FORMAT. * * @param fileType The _FILE_TYPE constant indicating the type of file. * @param formatOrByteSize The format of the file (one of the _FORMAT constants. In the case of LOCAL_FILE_TYPE, the byte size. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean setFileType(final int fileType, final int formatOrByteSize) throws IOException { if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize))) { this.fileType = fileType; this.fileFormat = formatOrByteSize; return true; } return false; } /** * Sets whether the IP address from the server's response should be used. Until 3.9.0, this has always been the case. Beginning with 3.9.0, that IP address * will be silently ignored, and replaced with the remote IP address of the control connection, unless this configuration option is given, which restores * the old behavior. To enable this by default, use the system property {@link FTPClient#FTP_IP_ADDRESS_FROM_PASV_RESPONSE}. * * @param usingIpAddressFromPasvResponse True, if the IP address from the server's response should be used (pre-3.9.0 compatible behavior), or false (ignore * that IP address). * @see FTPClient#FTP_IP_ADDRESS_FROM_PASV_RESPONSE * @see #isIpAddressFromPasvResponse * @since 3.9.0 */ public void setIpAddressFromPasvResponse(final boolean usingIpAddressFromPasvResponse) { this.ipAddressFromPasvResponse = usingIpAddressFromPasvResponse; } /** * You can set this to true if you would like to get hidden files when {@link #listFiles} too. A LIST -a will be issued to the ftp server. It * depends on your ftp server if you need to call this method, also dont expect to get rid of hidden files if you call this method with "false". * * @param listHiddenFiles true if hidden files should be listed * @since 2.0 */ public void setListHiddenFiles(final boolean listHiddenFiles) { this.listHiddenFiles = listHiddenFiles; } /** * Issue the FTP MFMT command (not supported by all servers) which sets the last modified time of a file. * * The timestamp should be in the form yyyyMMDDhhmmss. It should also be in GMT, but not all servers honor this. * * An FTP server would indicate its support of this feature by including "MFMT" in its response to the FEAT command, which may be retrieved by * FTPClient.features() * * @param pathname The file path for which last modified time is to be changed. * @param timeval The timestamp to set to, in yyyyMMDDhhmmss format. * @return true if successfully set, false if not * @throws IOException if an I/O error occurs. * @since 2.2 * @see http://tools.ietf.org/html/draft-somers-ftp-mfxx-04 */ public boolean setModificationTime(final String pathname, final String timeval) throws IOException { return (FTPReply.isPositiveCompletion(mfmt(pathname, timeval))); } /** * set the factory used for parser creation to the supplied factory object. * * @param parserFactory factory object used to create FTPFileEntryParsers * * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory */ public void setParserFactory(final FTPFileEntryParserFactory parserFactory) { this.parserFactory = parserFactory; } /** * Set the local IP address to use in passive mode. Useful when there are multiple network cards. * * @param inetAddress The local IP address of this machine. */ public void setPassiveLocalIPAddress(final InetAddress inetAddress) { this.passiveLocalHost = inetAddress; } /** * Set the local IP address to use in passive mode. Useful when there are multiple network cards. * * @param ipAddress The local IP address of this machine. * @throws UnknownHostException if the ipAddress cannot be resolved */ public void setPassiveLocalIPAddress(final String ipAddress) throws UnknownHostException { this.passiveLocalHost = InetAddress.getByName(ipAddress); } /** * Enables or disables passive mode NAT workaround. If enabled, a site-local PASV mode reply address will be replaced with the remote host address to which * the PASV mode request was sent (unless that is also a site local address). This gets around the problem that some NAT boxes may change the reply. *

* The default is true, i.e. site-local replies are replaced. *

* * @deprecated (3.6) use {@link #setPassiveNatWorkaroundStrategy(HostnameResolver)} instead * @param enabled true to enable replacing internal IP's in passive mode. */ @Deprecated public void setPassiveNatWorkaround(final boolean enabled) { this.passiveNatWorkaroundStrategy = enabled ? new NatServerResolverImpl(this) : null; } /** * Sets the workaround strategy to replace the PASV mode reply addresses. This gets around the problem that some NAT boxes may change the reply. * * The default implementation is {@code NatServerResolverImpl}, i.e. site-local replies are replaced. * * @param resolver strategy to replace internal IP's in passive mode or null to disable the workaround (i.e. use PASV mode reply address.) * @since 3.6 */ public void setPassiveNatWorkaroundStrategy(final HostnameResolver resolver) { this.passiveNatWorkaroundStrategy = resolver; } /** * Sets the value to be used for the data socket SO_RCVBUF option. If the value is positive, the option will be set when the data socket has been created. * * @param bufSize The size of the buffer, zero or negative means the value is ignored. * @since 3.3 */ public void setReceieveDataSocketBufferSize(final int bufSize) { receiveDataSocketBufferSize = bufSize; } /** * Enable or disable verification that the remote host taking part of a data connection is the same as the host to which the control connection is attached. * The default is for verification to be enabled. You may set this value at any time, whether the FTPClient is currently connected or not. * * @param enable True to enable verification, false to disable verification. */ public void setRemoteVerificationEnabled(final boolean enable) { remoteVerificationEnabled = enable; } /** * Sets the external IP address to report in EPRT/PORT commands in active mode. Useful when there are multiple network cards. * * @param ipAddress The external IP address of this machine. * @throws UnknownHostException if the ipAddress cannot be resolved * @since 3.1 * @see #getReportHostAddress() */ public void setReportActiveExternalIPAddress(final String ipAddress) throws UnknownHostException { this.reportActiveExternalHost = InetAddress.getByName(ipAddress); } /** * Sets the restart offset for file transfers. *

* The restart command is not sent to the server immediately. It is sent when a data connection is created as part of a subsequent command. The restart * marker is reset to zero after use. *

*

* Note: This method should only be invoked immediately prior to the transfer to which it applies. * * @param offset The offset into the remote file at which to start the next file transfer. This must be a value greater than or equal to zero. */ public void setRestartOffset(final long offset) { if (offset >= 0) { restartOffset = offset; } } /** * Sets the value to be used for the data socket SO_SNDBUF option. If the value is positive, the option will be set when the data socket has been created. * * @param bufSize The size of the buffer, zero or negative means the value is ignored. * @since 3.3 */ public void setSendDataSocketBufferSize(final int bufSize) { sendDataSocketBufferSize = bufSize; } /** * Set whether to use EPSV with IPv4. Might be worth enabling in some circumstances. * * For example, when using IPv4 with NAT it may work with some rare configurations. E.g. if FTP server has a static PASV address (external network) and the * client is coming from another internal network. In that case the data connection after PASV command would fail, while EPSV would make the client succeed * by taking just the port. * * @param selected value to set. * @since 2.2 */ public void setUseEPSVwithIPv4(final boolean selected) { this.useEPSVwithIPv4 = selected; } private boolean storeFile(final FTPCmd command, final String remote, final InputStream local) throws IOException { return _storeFile(command.getCommand(), remote, local); } /** * Stores a file on the server using the given name and taking input from the given InputStream. This method does NOT close the given InputStream. If the * current file type is ASCII, line separators in the file are transparently converted to the NETASCII format (i.e., you should not attempt to create a * special InputStream to do this). * * @param remote The name to give the remote file. * @param local The local InputStream from which to read the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some * other reason causing the server to send FTP reply code 421. This exception may be caught either as * an IOException or independently as itself. * @throws org.apache.commons.net.io.CopyStreamException If an I/O error occurs while actually transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException causing the error. This exception may * be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the * server. */ public boolean storeFile(final String remote, final InputStream local) throws IOException { return storeFile(FTPCmd.STOR, remote, local); } private OutputStream storeFileStream(final FTPCmd command, final String remote) throws IOException { return _storeFileStream(command.getCommand(), remote); } /** * Returns an OutputStream through which data can be written to store a file on the server using the given name. If the current file type is ASCII, the * returned OutputStream will convert line separators in the file to the NETASCII format (i.e., you should not attempt to create a special OutputStream to * do this). You must close the OutputStream when you finish writing to it. The OutputStream itself will take care of closing the parent data connection * socket upon being closed. *

* To finalize the file transfer you must call {@link #completePendingCommand completePendingCommand } and check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @param remote The name to give the remote file. * @return An OutputStream through which the remote file can be written. If the data connection cannot be opened (e.g., the file does not exist), null is * returned (in which case you may check the reply code to determine the exact reason for failure). * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public OutputStream storeFileStream(final String remote) throws IOException { return storeFileStream(FTPCmd.STOR, remote); } /** * Stores a file on the server using a unique name assigned by the server and taking input from the given InputStream. This method does NOT close the given * InputStream. If the current file type is ASCII, line separators in the file are transparently converted to the NETASCII format (i.e., you should not * attempt to create a special InputStream to do this). * * @param local The local InputStream from which to read the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some * other reason causing the server to send FTP reply code 421. This exception may be caught either as * an IOException or independently as itself. * @throws org.apache.commons.net.io.CopyStreamException If an I/O error occurs while actually transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException causing the error. This exception may * be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the * server. */ public boolean storeUniqueFile(final InputStream local) throws IOException { return storeFile(FTPCmd.STOU, null, local); } /** * Stores a file on the server using a unique name derived from the given name and taking input from the given InputStream. This method does NOT close the * given InputStream. If the current file type is ASCII, line separators in the file are transparently converted to the NETASCII format (i.e., you should * not attempt to create a special InputStream to do this). * * @param remote The name on which to base the unique name given to the remote file. * @param local The local InputStream from which to read the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some * other reason causing the server to send FTP reply code 421. This exception may be caught either as * an IOException or independently as itself. * @throws org.apache.commons.net.io.CopyStreamException If an I/O error occurs while actually transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException causing the error. This exception may * be caught either as an IOException or independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the * server. */ public boolean storeUniqueFile(final String remote, final InputStream local) throws IOException { return storeFile(FTPCmd.STOU, remote, local); } /** * Returns an OutputStream through which data can be written to store a file on the server using a unique name assigned by the server. If the current file * type is ASCII, the returned OutputStream will convert line separators in the file to the NETASCII format (i.e., you should not attempt to create a * special OutputStream to do this). You must close the OutputStream when you finish writing to it. The OutputStream itself will take care of closing the * parent data connection socket upon being closed. *

* To finalize the file transfer you must call {@link #completePendingCommand completePendingCommand } and check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @return An OutputStream through which the remote file can be written. If the data connection cannot be opened (e.g., the file does not exist), null is * returned (in which case you may check the reply code to determine the exact reason for failure). * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public OutputStream storeUniqueFileStream() throws IOException { return storeFileStream(FTPCmd.STOU, null); } /** * Returns an OutputStream through which data can be written to store a file on the server using a unique name derived from the given name. If the current * file type is ASCII, the returned OutputStream will convert line separators in the file to the NETASCII format (i.e., you should not attempt to create a * special OutputStream to do this). You must close the OutputStream when you finish writing to it. The OutputStream itself will take care of closing the * parent data connection socket upon being closed. *

* To finalize the file transfer you must call {@link #completePendingCommand completePendingCommand } and check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @param remote The name on which to base the unique name given to the remote file. * @return An OutputStream through which the remote file can be written. If the data connection cannot be opened (e.g., the file does not exist), null is * returned (in which case you may check the reply code to determine the exact reason for failure). * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public OutputStream storeUniqueFileStream(final String remote) throws IOException { return storeFileStream(FTPCmd.STOU, remote); } /** * Issue the FTP SMNT command. * * @param pathname The pathname to mount. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send FTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean structureMount(final String pathname) throws IOException { return FTPReply.isPositiveCompletion(smnt(pathname)); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java000066400000000000000000000666471434047722200323060ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.text.DateFormatSymbols; import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; import java.util.TreeMap; /** *

* This class implements an alternate means of configuring the {@link org.apache.commons.net.ftp.FTPClient FTPClient} object and also subordinate objects which * it uses. Any class implementing the {@link org.apache.commons.net.ftp.Configurable Configurable } interface can be configured by this object. *

*

* In particular this class was designed primarily to support configuration of FTP servers which express file timestamps in formats and languages other than * those for the US locale, which although it is the most common is not universal. Unfortunately, nothing in the FTP spec allows this to be determined in an * automated way, so manual configuration such as this is necessary. *

*

* This functionality was designed to allow existing clients to work exactly as before without requiring use of this component. This component should only need * to be explicitly invoked by the user of this package for problem cases that previous implementations could not solve. *

*

Examples of use of FTPClientConfig

Use cases: You are trying to access a server that *
    *
  • lists files with timestamps that use month names in languages other than English
  • *
  • lists files with timestamps that use date formats other than the American English "standard" MM dd yyyy
  • *
  • is in different time zone and you need accurate timestamps for dependency checking as in Ant
  • *
*

* Unpaged (whole list) access on a UNIX server that uses French month names but uses the "standard" MMM d yyyy date formatting * *

 * FTPClient f = FTPClient();
 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
 * conf.setServerLanguageCode("fr");
 * f.configure(conf);
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = listFiles(directory);
 * 
*

* Paged access on a UNIX server that uses Danish month names and "European" date formatting in Denmark's time zone, when you are in some other time zone. * *

 * FTPClient f = FTPClient();
 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
 * conf.setServerLanguageCode("da");
 * conf.setDefaultDateFormat("d MMM yyyy");
 * conf.setRecentDateFormat("d MMM HH:mm");
 * conf.setTimeZoneId("Europe/Copenhagen");
 * f.configure(conf);
 * f.connect(server);
 * f.login(username, password);
 * FTPListParseEngine engine = f.initiateListParsing("com.whatever.YourOwnParser", directory);
 *
 * while (engine.hasNext()) {
 *     FTPFile[] files = engine.getNext(25); // "page size" you want
 *     // do whatever you want with these files, display them, etc.
 *     // expensive FTPFile objects not created until needed.
 * }
 * 
*

* Unpaged (whole list) access on a VMS server that uses month names in a language not {@link #getSupportedLanguageCodes() supported} by the system. but uses * the "standard" MMM d yyyy date formatting * *

 * FTPClient f = FTPClient();
 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS);
 * conf.setShortMonthNames("jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des");
 * f.configure(conf);
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = listFiles(directory);
 * 
*

* Unpaged (whole list) access on a Windows-NT server in a different time zone. (Note, since the NT Format uses numeric date formatting, language issues are * irrelevant here). * *

 * FTPClient f = FTPClient();
 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
 * conf.setTimeZoneId("America/Denver");
 * f.configure(conf);
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = listFiles(directory);
 * 
* * Unpaged (whole list) access on a Windows-NT server in a different time zone but which has been configured to use a unix-style listing format. * *
 * FTPClient f = FTPClient();
 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
 * conf.setTimeZoneId("America/Denver");
 * f.configure(conf);
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = listFiles(directory);
 * 
* * @since 1.4 * @see org.apache.commons.net.ftp.Configurable * @see org.apache.commons.net.ftp.FTPClient * @see org.apache.commons.net.ftp.parser.FTPTimestampParserImpl#configure(FTPClientConfig) * @see org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl */ public class FTPClientConfig { /** * Identifier by which a unix-based ftp server is known throughout the commons-net ftp system. */ public static final String SYST_UNIX = "UNIX"; /** * Identifier for alternate UNIX parser; same as {@link #SYST_UNIX} but leading spaces are trimmed from file names. This is to maintain backwards * compatibility with the original behavior of the parser which ignored multiple spaces between the date and the start of the file name. * * @since 3.4 */ public static final String SYST_UNIX_TRIM_LEADING = "UNIX_LTRIM"; /** * Identifier by which a vms-based ftp server is known throughout the commons-net ftp system. */ public static final String SYST_VMS = "VMS"; /** * Identifier by which a WindowsNT-based ftp server is known throughout the commons-net ftp system. */ public static final String SYST_NT = "WINDOWS"; /** * Identifier by which an OS/2-based ftp server is known throughout the commons-net ftp system. */ public static final String SYST_OS2 = "OS/2"; /** * Identifier by which an OS/400-based ftp server is known throughout the commons-net ftp system. */ public static final String SYST_OS400 = "OS/400"; /** * Identifier by which an AS/400-based ftp server is known throughout the commons-net ftp system. */ public static final String SYST_AS400 = "AS/400"; /** * Identifier by which an MVS-based ftp server is known throughout the commons-net ftp system. */ public static final String SYST_MVS = "MVS"; /** * Some servers return an "UNKNOWN Type: L8" message in response to the SYST command. We set these to be a Unix-type system. This may happen if the ftpd in * question was compiled without system information. * * NET-230 - Updated to be UPPERCASE so that the check done in createFileEntryParser will succeed. * * @since 1.5 */ public static final String SYST_L8 = "TYPE: L8"; /** * Identifier by which an Netware-based ftp server is known throughout the commons-net ftp system. * * @since 1.5 */ public static final String SYST_NETWARE = "NETWARE"; /** * Identifier by which a Mac pre OS-X -based ftp server is known throughout the commons-net ftp system. * * @since 3.1 */ // Full string is "MACOS Peter's Server"; the substring below should be enough public static final String SYST_MACOS_PETER = "MACOS PETER"; // NET-436 private static final Map LANGUAGE_CODE_MAP = new TreeMap<>(); static { // if there are other commonly used month name encodings which // correspond to particular locales, please add them here. // many locales code short names for months as all three letters // these we handle simply. LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH); LANGUAGE_CODE_MAP.put("de", Locale.GERMAN); LANGUAGE_CODE_MAP.put("it", Locale.ITALIAN); LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian // some don't LANGUAGE_CODE_MAP.put("fr", "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c"); // french } /** * Returns a DateFormatSymbols object configured with short month names as in the supplied string * * @param shortmonths This should be as described in {@link #setShortMonthNames(String) shortMonthNames} * @return a DateFormatSymbols object configured with short month names as in the supplied string */ public static DateFormatSymbols getDateFormatSymbols(final String shortmonths) { final String[] months = splitShortMonthString(shortmonths); final DateFormatSymbols dfs = new DateFormatSymbols(Locale.US); dfs.setShortMonths(months); return dfs; } /** * Returns a Collection of all the language codes currently supported by this class. See {@link #setServerLanguageCode(String) serverLanguageCode} for a * functional descrption of language codes within this system. * * @return a Collection of all the language codes currently supported by this class */ public static Collection getSupportedLanguageCodes() { return LANGUAGE_CODE_MAP.keySet(); } /** * Looks up the supplied language code in the internally maintained table of language codes. Returns a DateFormatSymbols object configured with short month * names corresponding to the code. If there is no corresponding entry in the table, the object returned will be that for Locale.US * * @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode} * @return a DateFormatSymbols object configured with short month names corresponding to the supplied code, or with month names for Locale.US * if there is no corresponding entry in the internal table. */ public static DateFormatSymbols lookupDateFormatSymbols(final String languageCode) { final Object lang = LANGUAGE_CODE_MAP.get(languageCode); if (lang != null) { if (lang instanceof Locale) { return new DateFormatSymbols((Locale) lang); } if (lang instanceof String) { return getDateFormatSymbols((String) lang); } } return new DateFormatSymbols(Locale.US); } private static String[] splitShortMonthString(final String shortmonths) { final StringTokenizer st = new StringTokenizer(shortmonths, "|"); final int monthcnt = st.countTokens(); if (12 != monthcnt) { throw new IllegalArgumentException("expecting a pipe-delimited string containing 12 tokens"); } final String[] months = new String[13]; int pos = 0; while (st.hasMoreTokens()) { months[pos++] = st.nextToken(); } months[pos] = ""; return months; } private final String serverSystemKey; private String defaultDateFormatStr; private String recentDateFormatStr; private boolean lenientFutureDates = true; // NET-407 private String serverLanguageCode; private String shortMonthNames; private String serverTimeZoneId; private boolean saveUnparseableEntries; /** * Convenience constructor mainly for use in testing. Constructs a UNIX configuration. */ public FTPClientConfig() { this(SYST_UNIX); } /** * Copy constructor * * @param config source * @since 3.6 */ public FTPClientConfig(final FTPClientConfig config) { this.serverSystemKey = config.serverSystemKey; this.defaultDateFormatStr = config.defaultDateFormatStr; this.lenientFutureDates = config.lenientFutureDates; this.recentDateFormatStr = config.recentDateFormatStr; this.saveUnparseableEntries = config.saveUnparseableEntries; this.serverLanguageCode = config.serverLanguageCode; this.serverTimeZoneId = config.serverTimeZoneId; this.shortMonthNames = config.shortMonthNames; } /** * The main constructor for an FTPClientConfig object * * @param systemKey key representing system type of the server being connected to. See {@link #getServerSystemKey() serverSystemKey} If set to the empty * string, then FTPClient uses the system type returned by the server. However this is not recommended for general use; the correct system * type should be set if it is known. */ public FTPClientConfig(final String systemKey) { this.serverSystemKey = systemKey; } // Copy constructor, intended for use by FTPClient only FTPClientConfig(final String systemKey, final FTPClientConfig config) { this.serverSystemKey = systemKey; this.defaultDateFormatStr = config.defaultDateFormatStr; this.lenientFutureDates = config.lenientFutureDates; this.recentDateFormatStr = config.recentDateFormatStr; this.saveUnparseableEntries = config.saveUnparseableEntries; this.serverLanguageCode = config.serverLanguageCode; this.serverTimeZoneId = config.serverTimeZoneId; this.shortMonthNames = config.shortMonthNames; } /** * Constructor which allows setting of the format string member fields * * @param systemKey key representing system type of the server being connected to. See {@link #getServerSystemKey() serverSystemKey} * @param defaultDateFormatStr See {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} * @param recentDateFormatStr See {@link #setRecentDateFormatStr(String) recentDateFormatStr} * @since 3.6 */ public FTPClientConfig(final String systemKey, final String defaultDateFormatStr, final String recentDateFormatStr) { this(systemKey); this.defaultDateFormatStr = defaultDateFormatStr; this.recentDateFormatStr = recentDateFormatStr; } /** * Constructor which allows setting of most member fields * * @param systemKey key representing system type of the server being connected to. See {@link #getServerSystemKey() serverSystemKey} * @param defaultDateFormatStr See {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} * @param recentDateFormatStr See {@link #setRecentDateFormatStr(String) recentDateFormatStr} * @param serverLanguageCode See {@link #setServerLanguageCode(String) serverLanguageCode} * @param shortMonthNames See {@link #setShortMonthNames(String) shortMonthNames} * @param serverTimeZoneId See {@link #setServerTimeZoneId(String) serverTimeZoneId} */ public FTPClientConfig(final String systemKey, final String defaultDateFormatStr, final String recentDateFormatStr, final String serverLanguageCode, final String shortMonthNames, final String serverTimeZoneId) { this(systemKey); this.defaultDateFormatStr = defaultDateFormatStr; this.recentDateFormatStr = recentDateFormatStr; this.serverLanguageCode = serverLanguageCode; this.shortMonthNames = shortMonthNames; this.serverTimeZoneId = serverTimeZoneId; } /** * Constructor which allows setting of all member fields * * @param systemKey key representing system type of the server being connected to. See {@link #getServerSystemKey() serverSystemKey} * @param defaultDateFormatStr See {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} * @param recentDateFormatStr See {@link #setRecentDateFormatStr(String) recentDateFormatStr} * @param serverLanguageCode See {@link #setServerLanguageCode(String) serverLanguageCode} * @param shortMonthNames See {@link #setShortMonthNames(String) shortMonthNames} * @param serverTimeZoneId See {@link #setServerTimeZoneId(String) serverTimeZoneId} * @param lenientFutureDates See {@link #setLenientFutureDates(boolean) lenientFutureDates} * @param saveUnparseableEntries See {@link #setUnparseableEntries(boolean) saveUnparseableEntries} */ public FTPClientConfig(final String systemKey, final String defaultDateFormatStr, final String recentDateFormatStr, final String serverLanguageCode, final String shortMonthNames, final String serverTimeZoneId, final boolean lenientFutureDates, final boolean saveUnparseableEntries) { this(systemKey); this.defaultDateFormatStr = defaultDateFormatStr; this.lenientFutureDates = lenientFutureDates; this.recentDateFormatStr = recentDateFormatStr; this.saveUnparseableEntries = saveUnparseableEntries; this.serverLanguageCode = serverLanguageCode; this.shortMonthNames = shortMonthNames; this.serverTimeZoneId = serverTimeZoneId; } /** * getter for the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} property. * * @return Returns the defaultDateFormatStr property. */ public String getDefaultDateFormatStr() { return defaultDateFormatStr; } /** * getter for the {@link #setRecentDateFormatStr(String) recentDateFormatStr} property. * * @return Returns the recentDateFormatStr property. */ public String getRecentDateFormatStr() { return recentDateFormatStr; } /** *

* getter for the {@link #setServerLanguageCode(String) serverLanguageCode} property. *

* * @return Returns the serverLanguageCode property. */ public String getServerLanguageCode() { return serverLanguageCode; } /** * Getter for the serverSystemKey property. This property specifies the general type of server to which the client connects. Should be either one of the * FTPClientConfig.SYST_* codes or else the fully qualified class name of a parser implementing both the FTPFileEntryParser and * Configurable interfaces. * * @return Returns the serverSystemKey property. */ public String getServerSystemKey() { return serverSystemKey; } /** * getter for the {@link #setServerTimeZoneId(String) serverTimeZoneId} property. * * @return Returns the serverTimeZoneId property. */ public String getServerTimeZoneId() { return serverTimeZoneId; } /** *

* getter for the {@link #setShortMonthNames(String) shortMonthNames} property. *

* * @return Returns the shortMonthNames. */ public String getShortMonthNames() { return shortMonthNames; } /** * @return true if list parsing should return FTPFile entries even for unparseable response lines *

* If true, the FTPFile for any unparseable entries will contain only the unparsed entry {@link FTPFile#getRawListing()} and * {@link FTPFile#isValid()} will return {@code false} * @since 3.4 */ public boolean getUnparseableEntries() { return this.saveUnparseableEntries; } /** *

* getter for the {@link #setLenientFutureDates(boolean) lenientFutureDates} property. *

* * @return Returns the lenientFutureDates (default true). * @since 1.5 */ public boolean isLenientFutureDates() { return lenientFutureDates; } /** *

* setter for the defaultDateFormatStr property. This property specifies the main date format that will be used by a parser configured by this configuration * to parse file timestamps. If this is not specified, such a parser will use as a default value, the most commonly used format which will be in as used in * en_US locales. *

*

* This should be in the format described for java.text.SimpleDateFormat. property. *

* * @param defaultDateFormatStr The defaultDateFormatStr to set. */ public void setDefaultDateFormatStr(final String defaultDateFormatStr) { this.defaultDateFormatStr = defaultDateFormatStr; } /** *

* setter for the lenientFutureDates property. This boolean property (default: true) only has meaning when a {@link #setRecentDateFormatStr(String) * recentDateFormatStr} property has been set. In that case, if this property is set true, then the parser, when it encounters a listing parseable with the * recent date format, will only consider a date to belong to the previous year if it is more than one day in the future. This will allow all out-of-synch * situations (whether based on "slop" - i.e. servers simply out of synch with one another or because of time zone differences - but in the latter case it * is highly recommended to use the {@link #setServerTimeZoneId(String) serverTimeZoneId} property instead) to resolve correctly. *

*

* This is used primarily in unix-based systems. *

* * @param lenientFutureDates set true to compensate for out-of-synch conditions. */ public void setLenientFutureDates(final boolean lenientFutureDates) { this.lenientFutureDates = lenientFutureDates; } /** *

* setter for the recentDateFormatStr property. This property specifies a secondary date format that will be used by a parser configured by this * configuration to parse file timestamps, typically those less than a year old. If this is not specified, such a parser will not attempt to parse using an * alternate format. *

*

* This is used primarily in unix-based systems. *

*

* This should be in the format described for java.text.SimpleDateFormat. *

* * @param recentDateFormatStr The recentDateFormatStr to set. */ public void setRecentDateFormatStr(final String recentDateFormatStr) { this.recentDateFormatStr = recentDateFormatStr; } /** *

* setter for the serverLanguageCode property. This property allows user to specify a * two-letter ISO-639 language code that will be used to configure the set of month names used by the file timestamp parser. If neither this nor the * {@link #setShortMonthNames(String) shortMonthNames} is specified, parsing will assume English month names, which may or may not be significant, depending * on whether the date format(s) specified via {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or {@link #setRecentDateFormatStr(String) * recentDateFormatStr} are using numeric or alphabetic month names. *

*

* If the code supplied is not supported here, en_US month names will be used. We are supporting here those language codes which, when a * java.util.Locale is constucted using it, and a java.text.SimpleDateFormat is constructed using that Locale, the array returned * by the SimpleDateFormat's getShortMonths() method consists solely of three 8-bit ASCII character strings. Additionally, languages which do * not meet this requirement are included if a common alternative set of short month names is known to be used. This means that users who can tell us of * additional such encodings may get them added to the list of supported languages by contacting the Apache Commons Net team. *

*

* Please note that this attribute will NOT be used to determine a locale-based date format for the language. Experience has shown that * many if not most FTP servers outside the United States employ the standard en_US date format orderings of MMM d yyyy and * MMM d HH:mm and attempting to deduce this automatically here would cause more problems than it would solve. The date format must be changed * via the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or {@link #setRecentDateFormatStr(String) recentDateFormatStr} parameters. *

* * @param serverLanguageCode The value to set to the serverLanguageCode property. */ public void setServerLanguageCode(final String serverLanguageCode) { this.serverLanguageCode = serverLanguageCode; } /** *

* setter for the serverTimeZoneId property. This property allows a time zone to be specified corresponding to that known to be used by an FTP server in * file listings. This might be particularly useful to clients such as Ant that try to use these timestamps for dependency checking. *

*

* This should be one of the identifiers used by java.util.TimeZone to refer to time zones, for example, America/Chicago or * Asia/Rangoon. *

* * @param serverTimeZoneId The serverTimeZoneId to set. */ public void setServerTimeZoneId(final String serverTimeZoneId) { this.serverTimeZoneId = serverTimeZoneId; } /** *

* setter for the shortMonthNames property. This property allows the user to specify a set of month names used by the server that is different from those * that may be specified using the {@link #setServerLanguageCode(String) serverLanguageCode} property. *

*

* This should be a string containing twelve strings each composed of three characters, delimited by pipe (|) characters. Currently, only 8-bit ASCII * characters are known to be supported. For example, a set of month names used by a hypothetical Icelandic FTP server might conceivably be specified as * "jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des". *

* * @param shortMonthNames The value to set to the shortMonthNames property. */ public void setShortMonthNames(final String shortMonthNames) { this.shortMonthNames = shortMonthNames; } /** * Allow list parsing methods to create basic FTPFile entries if parsing fails. *

* In this case, the FTPFile will contain only the unparsed entry {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false} * * @param saveUnparseable if true, then create FTPFile entries if parsing fails * @since 3.4 */ public void setUnparseableEntries(final boolean saveUnparseable) { this.saveUnparseableEntries = saveUnparseable; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPCmd.java000066400000000000000000000061641434047722200304310ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.ftp; /** * @since 3.3 */ public enum FTPCmd { ABOR, ACCT, ALLO, APPE, CDUP, CWD, DELE, EPRT, EPSV, FEAT, HELP, LIST, MDTM, MFMT, MKD, MLSD, MLST, MODE, NLST, NOOP, PASS, PASV, PORT, PWD, QUIT, REIN, REST, RETR, RMD, RNFR, RNTO, SITE, /** @since 3.7 */ SIZE, SMNT, STAT, STOR, STOU, STRU, SYST, TYPE, USER,; // Aliases public static final FTPCmd ABORT = ABOR; public static final FTPCmd ACCOUNT = ACCT; public static final FTPCmd ALLOCATE = ALLO; public static final FTPCmd APPEND = APPE; public static final FTPCmd CHANGE_TO_PARENT_DIRECTORY = CDUP; public static final FTPCmd CHANGE_WORKING_DIRECTORY = CWD; public static final FTPCmd DATA_PORT = PORT; public static final FTPCmd DELETE = DELE; public static final FTPCmd FEATURES = FEAT; public static final FTPCmd FILE_STRUCTURE = STRU; public static final FTPCmd GET_MOD_TIME = MDTM; public static final FTPCmd LOGOUT = QUIT; public static final FTPCmd MAKE_DIRECTORY = MKD; public static final FTPCmd MOD_TIME = MDTM; public static final FTPCmd NAME_LIST = NLST; public static final FTPCmd PASSIVE = PASV; public static final FTPCmd PASSWORD = PASS; public static final FTPCmd PRINT_WORKING_DIRECTORY = PWD; public static final FTPCmd REINITIALIZE = REIN; public static final FTPCmd REMOVE_DIRECTORY = RMD; public static final FTPCmd RENAME_FROM = RNFR; public static final FTPCmd RENAME_TO = RNTO; public static final FTPCmd REPRESENTATION_TYPE = TYPE; public static final FTPCmd RESTART = REST; public static final FTPCmd RETRIEVE = RETR; public static final FTPCmd SET_MOD_TIME = MFMT; public static final FTPCmd SITE_PARAMETERS = SITE; public static final FTPCmd STATUS = STAT; public static final FTPCmd STORE = STOR; public static final FTPCmd STORE_UNIQUE = STOU; public static final FTPCmd STRUCTURE_MOUNT = SMNT; public static final FTPCmd SYSTEM = SYST; public static final FTPCmd TRANSFER_MODE = MODE; public static final FTPCmd USERNAME = USER; /** * Retrieve the FTP protocol command string corresponding to a specified command code. * * @return The FTP protcol command string corresponding to a specified command code. */ public final String getCommand() { return this.name(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPCommand.java000066400000000000000000000141611434047722200313000ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; /** * FTPCommand stores a set of constants for FTP command codes. To interpret the meaning of the codes, familiarity with RFC 959 is assumed. The mnemonic constant * names are transcriptions from the code descriptions of RFC 959. For those who think in terms of the actual FTP commands, a set of constants such as * {@link #USER USER } are provided where the constant name is the same as the FTP command. * * @deprecated use {@link FTPCmd} instead */ @Deprecated public final class FTPCommand { public static final int USER = 0; public static final int PASS = 1; public static final int ACCT = 2; public static final int CWD = 3; public static final int CDUP = 4; public static final int SMNT = 5; public static final int REIN = 6; public static final int QUIT = 7; public static final int PORT = 8; public static final int PASV = 9; public static final int TYPE = 10; public static final int STRU = 11; public static final int MODE = 12; public static final int RETR = 13; public static final int STOR = 14; public static final int STOU = 15; public static final int APPE = 16; public static final int ALLO = 17; public static final int REST = 18; public static final int RNFR = 19; public static final int RNTO = 20; public static final int ABOR = 21; public static final int DELE = 22; public static final int RMD = 23; public static final int MKD = 24; public static final int PWD = 25; public static final int LIST = 26; public static final int NLST = 27; public static final int SITE = 28; public static final int SYST = 29; public static final int STAT = 30; public static final int HELP = 31; public static final int NOOP = 32; /** @since 2.0 */ public static final int MDTM = 33; /** @since 2.2 */ public static final int FEAT = 34; /** @since 2.2 */ public static final int MFMT = 35; /** @since 2.2 */ public static final int EPSV = 36; /** @since 2.2 */ public static final int EPRT = 37; /** * Machine parseable list for a directory * * @since 3.0 */ public static final int MLSD = 38; /** * Machine parseable list for a single file * * @since 3.0 */ public static final int MLST = 39; // Must agree with final entry above; used to check array size private static final int LAST = MLST; public static final int USERNAME = USER; public static final int PASSWORD = PASS; public static final int ACCOUNT = ACCT; public static final int CHANGE_WORKING_DIRECTORY = CWD; public static final int CHANGE_TO_PARENT_DIRECTORY = CDUP; public static final int STRUCTURE_MOUNT = SMNT; public static final int REINITIALIZE = REIN; public static final int LOGOUT = QUIT; public static final int DATA_PORT = PORT; public static final int PASSIVE = PASV; public static final int REPRESENTATION_TYPE = TYPE; public static final int FILE_STRUCTURE = STRU; public static final int TRANSFER_MODE = MODE; public static final int RETRIEVE = RETR; public static final int STORE = STOR; public static final int STORE_UNIQUE = STOU; public static final int APPEND = APPE; public static final int ALLOCATE = ALLO; public static final int RESTART = REST; public static final int RENAME_FROM = RNFR; public static final int RENAME_TO = RNTO; public static final int ABORT = ABOR; public static final int DELETE = DELE; public static final int REMOVE_DIRECTORY = RMD; public static final int MAKE_DIRECTORY = MKD; public static final int PRINT_WORKING_DIRECTORY = PWD; // public static final int LIST = LIST; public static final int NAME_LIST = NLST; public static final int SITE_PARAMETERS = SITE; public static final int SYSTEM = SYST; public static final int STATUS = STAT; // public static final int HELP = HELP; // public static final int NOOP = NOOP; /** @since 2.0 */ public static final int MOD_TIME = MDTM; /** @since 2.2 */ public static final int FEATURES = FEAT; /** @since 2.2 */ public static final int GET_MOD_TIME = MDTM; /** @since 2.2 */ public static final int SET_MOD_TIME = MFMT; private static final String[] COMMANDS = { "USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT", "REIN", "QUIT", "PORT", "PASV", "TYPE", "STRU", "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO", "REST", "RNFR", "RNTO", "ABOR", "DELE", "RMD", "MKD", "PWD", "LIST", "NLST", "SITE", "SYST", "STAT", "HELP", "NOOP", "MDTM", "FEAT", "MFMT", "EPSV", "EPRT", "MLSD", "MLST" }; // default access needed for Unit test static void checkArray() { final int expectedLength = LAST + 1; if (COMMANDS.length != expectedLength) { throw new RuntimeException("Incorrect _commands array. Should have length " + expectedLength + " found " + COMMANDS.length); } } /** * Retrieve the FTP protocol command string corresponding to a specified command code. * * @param command The command code. * @return The FTP protcol command string corresponding to a specified command code. */ public static String getCommand(final int command) { return COMMANDS[command]; } // Cannot be instantiated private FTPCommand() { } } FTPConnectionClosedException.java000066400000000000000000000035201434047722200347500ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.IOException; /** * FTPConnectionClosedException is used to indicate the premature or unexpected closing of an FTP connection resulting from a * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } response (FTP reply code 421) to a failed FTP command. This * exception is derived from IOException and therefore may be caught either as an IOException or specifically as an FTPConnectionClosedException. * * @see FTP * @see FTPClient */ public class FTPConnectionClosedException extends IOException { private static final long serialVersionUID = 3500547241659379952L; /** Constructs a FTPConnectionClosedException with no message */ public FTPConnectionClosedException() { } /** * Constructs a FTPConnectionClosedException with a specified message. * * @param message The message explaining the reason for the exception. */ public FTPConnectionClosedException(final String message) { super(message); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPFile.java000066400000000000000000000371531434047722200306070ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.Serializable; import java.time.Instant; import java.util.Calendar; import java.util.Date; import java.util.Formatter; import java.util.TimeZone; /** * The FTPFile class is used to represent information about files stored on an FTP server. * * @see FTPFileEntryParser * @see FTPClient#listFiles */ public class FTPFile implements Serializable { private static final long serialVersionUID = 9010790363003271996L; /** A constant indicating an FTPFile is a file. */ public static final int FILE_TYPE = 0; /** A constant indicating an FTPFile is a directory. */ public static final int DIRECTORY_TYPE = 1; /** A constant indicating an FTPFile is a symbolic link. */ public static final int SYMBOLIC_LINK_TYPE = 2; /** A constant indicating an FTPFile is of unknown type. */ public static final int UNKNOWN_TYPE = 3; /** A constant indicating user access permissions. */ public static final int USER_ACCESS = 0; /** A constant indicating group access permissions. */ public static final int GROUP_ACCESS = 1; /** A constant indicating world access permissions. */ public static final int WORLD_ACCESS = 2; /** A constant indicating file/directory read permission. */ public static final int READ_PERMISSION = 0; /** A constant indicating file/directory write permission. */ public static final int WRITE_PERMISSION = 1; /** * A constant indicating file execute permission or directory listing permission. */ public static final int EXECUTE_PERMISSION = 2; private int type = UNKNOWN_TYPE; /** 0 is invalid as a link count. */ private int hardLinkCount; /** 0 is valid, so use -1. */ private long size = -1; private String rawListing; private String user = ""; private String group = ""; private String name; private String link; // TODO Consider changing internal representation to java.time. private Calendar calendar; /** If this is null, then list entry parsing failed. */ private final boolean[][] permissions; // e.g. _permissions[USER_ACCESS][READ_PERMISSION] /** Creates an empty FTPFile. */ public FTPFile() { permissions = new boolean[3][3]; } /** * Constructor for use by {@link FTPListParseEngine} only. Used to create FTPFile entries for failed parses * * @param rawListing line that could not be parsed. * @since 3.4 */ FTPFile(final String rawListing) { this.permissions = null; // flag that entry is invalid this.rawListing = rawListing; } private char formatType() { switch (type) { case FILE_TYPE: return '-'; case DIRECTORY_TYPE: return 'd'; case SYMBOLIC_LINK_TYPE: return 'l'; default: return '?'; } } /** * Gets the name of the group owning the file. Sometimes this will be a string representation of the group number. * * @return The name of the group owning the file. */ public String getGroup() { return group; } /** * Gets the number of hard links to this file. This is not to be confused with symbolic links. * * @return The number of hard links to this file. */ public int getHardLinkCount() { return hardLinkCount; } /** * If the FTPFile is a symbolic link, this method returns the name of the file being pointed to by the symbolic link. Otherwise it returns null. * * @return The file pointed to by the symbolic link (null if the FTPFile is not a symbolic link). */ public String getLink() { return link; } /** * Gets the name of the file. * * @return The name of the file. */ public String getName() { return name; } /** * Gets the original FTP server raw listing used to initialize the FTPFile. * * @return The original FTP server raw listing used to initialize the FTPFile. */ public String getRawListing() { return rawListing; } /** * Gets the file size in bytes. * * @return The file size in bytes. */ public long getSize() { return size; } /** * Gets the file timestamp. This usually the last modification time. * * @return A Calendar instance representing the file timestamp. */ public Calendar getTimestamp() { return calendar; } /** * Gets the file timestamp. This usually the last modification time. * * @return A Calendar instance representing the file timestamp. * @since 3.9.0 */ public Instant getTimestampInstant() { return calendar == null ? null : calendar.toInstant(); } /** * Gets the type of the file (one of the _TYPE constants), e.g., if it is a directory, a regular file, or a symbolic link. * * @return The type of the file. */ public int getType() { return type; } /** * Gets the name of the user owning the file. Sometimes this will be a string representation of the user number. * * @return The name of the user owning the file. */ public String getUser() { return user; } /** * Tests if the given access group (one of the _ACCESS constants) has the given access permission (one of the _PERMISSION * constants) to the file. * * @param access The access group (one of the _ACCESS constants) * @param permission The access permission (one of the _PERMISSION constants) * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range * @return true if {@link #isValid()} is {@code true &&} the associated permission is set; {@code false} otherwise. */ public boolean hasPermission(final int access, final int permission) { if (permissions == null) { return false; } return permissions[access][permission]; } /** * Tests if the file is a directory. * * @return True if the file is of type DIRECTORY_TYPE, false if not. */ public boolean isDirectory() { return type == DIRECTORY_TYPE; } /** * Tests if the file is a regular file. * * @return True if the file is of type FILE_TYPE, false if not. */ public boolean isFile() { return type == FILE_TYPE; } /** * Tests if the file is a symbolic link. * * @return True if the file is of type UNKNOWN_TYPE, false if not. */ public boolean isSymbolicLink() { return type == SYMBOLIC_LINK_TYPE; } /** * Tests if the type of the file is unknown. * * @return True if the file is of type UNKNOWN_TYPE, false if not. */ public boolean isUnknown() { return type == UNKNOWN_TYPE; } /** * Tests whether an entry is valid or not. If the entry is invalid, only the {@link #getRawListing()} method will be useful. Other methods may fail. * * Used in conjunction with list parsing that preseverves entries that failed to parse. * * @see FTPClientConfig#setUnparseableEntries(boolean) * @return true if the entry is valid * @since 3.4 */ public boolean isValid() { return permissions != null; } private String permissionToString(final int access) { final StringBuilder sb = new StringBuilder(); if (hasPermission(access, READ_PERMISSION)) { sb.append('r'); } else { sb.append('-'); } if (hasPermission(access, WRITE_PERMISSION)) { sb.append('w'); } else { sb.append('-'); } if (hasPermission(access, EXECUTE_PERMISSION)) { sb.append('x'); } else { sb.append('-'); } return sb.toString(); } private void readObject(final java.io.ObjectInputStream in) { throw new UnsupportedOperationException("Serialization is not supported"); } /** * Sets the name of the group owning the file. This may be a string representation of the group number. * * @param group The name of the group owning the file. */ public void setGroup(final String group) { this.group = group; } /** * Sets the number of hard links to this file. This is not to be confused with symbolic links. * * @param links The number of hard links to this file. */ public void setHardLinkCount(final int links) { this.hardLinkCount = links; } /** * If the FTPFile is a symbolic link, use this method to set the name of the file being pointed to by the symbolic link. * * @param link The file pointed to by the symbolic link. */ public void setLink(final String link) { this.link = link; } /** * Sets the name of the file. * * @param name The name of the file. */ public void setName(final String name) { this.name = name; } /** * Sets if the given access group (one of the _ACCESS constants) has the given access permission (one of the _PERMISSION * constants) to the file. * * @param access The access group (one of the _ACCESS constants) * @param permission The access permission (one of the _PERMISSION constants) * @param value True if permission is allowed, false if not. * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range */ public void setPermission(final int access, final int permission, final boolean value) { permissions[access][permission] = value; } /** * Sets the original FTP server raw listing from which the FTPFile was created. * * @param rawListing The raw FTP server listing. */ public void setRawListing(final String rawListing) { this.rawListing = rawListing; } /** * Sets the file size in bytes. * * @param size The file size in bytes. */ public void setSize(final long size) { this.size = size; } /** * Sets the file timestamp. This usually the last modification time. The parameter is not cloned, so do not alter its value after calling this method. * * @param date A Calendar instance representing the file timestamp. */ public void setTimestamp(final Calendar date) { this.calendar = date; } /** * Sets the type of the file (DIRECTORY_TYPE, FILE_TYPE, etc.). * * @param type The integer code representing the type of the file. */ public void setType(final int type) { this.type = type; } /** * Sets the name of the user owning the file. This may be a string representation of the user number; * * @param user The name of the user owning the file. */ public void setUser(final String user) { this.user = user; } /** * Gets a string representation of the FTPFile information. This currently mimics the Unix listing format. This method uses the time zone of the Calendar * entry, which is the server time zone (if one was provided) otherwise it is the local time zone. *

* Note: if the instance is not valid {@link #isValid()}, no useful information can be returned. In this case, use {@link #getRawListing()} instead. *

* * @return A string representation of the FTPFile information. * @since 3.0 */ public String toFormattedString() { return toFormattedString(null); } /** * Gets a string representation of the FTPFile information. This currently mimics the Unix listing format. This method allows the Calendar time zone to be * overridden. *

* Note: if the instance is not valid {@link #isValid()}, no useful information can be returned. In this case, use {@link #getRawListing()} instead. *

* * @param timezone the time zone to use for displaying the time stamp If {@code null}, then use the Calendar entry * * @return A string representation of the FTPFile information. * @since 3.4 */ public String toFormattedString(final String timezone) { if (!isValid()) { return "[Invalid: could not parse file entry]"; } final StringBuilder sb = new StringBuilder(); try (final Formatter fmt = new Formatter(sb)) { sb.append(formatType()); sb.append(permissionToString(USER_ACCESS)); sb.append(permissionToString(GROUP_ACCESS)); sb.append(permissionToString(WORLD_ACCESS)); fmt.format(" %4d", Integer.valueOf(getHardLinkCount())); fmt.format(" %-8s %-8s", getUser(), getGroup()); fmt.format(" %8d", Long.valueOf(getSize())); Calendar timestamp = getTimestamp(); if (timestamp != null) { if (timezone != null) { final TimeZone newZone = TimeZone.getTimeZone(timezone); if (!newZone.equals(timestamp.getTimeZone())) { final Date original = timestamp.getTime(); final Calendar newStamp = Calendar.getInstance(newZone); newStamp.setTime(original); timestamp = newStamp; } } fmt.format(" %1$tY-%1$tm-%1$td", timestamp); // Only display time units if they are present if (timestamp.isSet(Calendar.HOUR_OF_DAY)) { fmt.format(" %1$tH", timestamp); if (timestamp.isSet(Calendar.MINUTE)) { fmt.format(":%1$tM", timestamp); if (timestamp.isSet(Calendar.SECOND)) { fmt.format(":%1$tS", timestamp); if (timestamp.isSet(Calendar.MILLISECOND)) { fmt.format(".%1$tL", timestamp); } } } fmt.format(" %1$tZ", timestamp); } } sb.append(' '); sb.append(getName()); } return sb.toString(); } /* * Serialization is unnecessary for this class. Reject attempts to do so until such time as the Serializable attribute can be dropped. */ /** * Gets a string representation of the FTPFile information. * * @return A string representation of the FTPFile information. */ @Override public String toString() { return getRawListing(); } private void writeObject(final java.io.ObjectOutputStream out) { throw new UnsupportedOperationException("Serialization is not supported"); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java000066400000000000000000000111341434047722200327750ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.BufferedReader; import java.io.IOException; import java.util.List; /** * FTPFileEntryParser defines the interface for parsing a single FTP file listing and converting that information into an * {@link org.apache.commons.net.ftp.FTPFile} instance. Sometimes you will want to parse unusual listing formats, in which case you would create your own * implementation of FTPFileEntryParser and if necessary, subclass FTPFile. *

* Here are some examples showing how to use one of the classes that implement this interface. *

* * The first example uses the FTPClient.listFiles() API to pull the whole list from the subfolder subfolder in one call, attempting to * automatically detect the parser type. This method, without a parserKey parameter, indicates that autodection should be used. * *

 * FTPClient f = FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = f.listFiles("subfolder");
 * 
* * The second example uses the FTPClient.listFiles() API to pull the whole list from the current working directory in one call, but specifying by * classname the parser to be used. For this particular parser class, this approach is necessary since there is no way to autodetect this server type. * *
 * FTPClient f = FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = f.listFiles("org.apache.commons.net.ftp.parser.EnterpriseUnixFTPFileEntryParser", ".");
 * 
* * The third example uses the FTPClient.listFiles() API to pull a single file listing in an arbitrary directory in one call, specifying by KEY the * parser to be used, in this case, VMS. * *
 * FTPClient f = FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = f.listFiles("VMS", "subfolder/foo.java");
 * 
* * For an alternative approach, see the {@link FTPListParseEngine} class which provides iterative access. * * @see org.apache.commons.net.ftp.FTPFile * @see org.apache.commons.net.ftp.FTPClient#listFiles() */ public interface FTPFileEntryParser { /** * Parses a line of an FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the file listing * line doesn't describe a file, null should be returned, otherwise a FTPFile instance representing the files in the directory * is returned. * * @param listEntry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ FTPFile parseFTPEntry(String listEntry); /** * This method is a hook for those implementors (such as VMSVersioningFTPEntryParser, and possibly others) which need to perform some action upon the * FTPFileList after it has been created from the server stream, but before any clients see the list. * * The default implementation can be a no-op. * * @param original Original list after it has been created from the server stream * * @return Original list as processed by this method. */ List preParse(List original); /** * Reads the next entry using the supplied BufferedReader object up to whatever delimits one entry from the next. Implementors must define this for the * particular ftp system being parsed. In many but not all cases, this can be defined simply by calling BufferedReader.readLine(). * * @param reader The BufferedReader object from which entries are to be read. * * @return A string representing the next ftp entry or null if none found. * @throws IOException thrown on any IO Error reading from the reader. */ String readNextEntry(BufferedReader reader) throws IOException; } FTPFileEntryParserImpl.java000066400000000000000000000047271434047722200335520ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.BufferedReader; import java.io.IOException; import java.util.List; /** * This abstract class implements both the older FTPFileListParser and newer FTPFileEntryParser interfaces with default functionality. All the classes in the * parser subpackage inherit from this. * */ public abstract class FTPFileEntryParserImpl implements FTPFileEntryParser { /** * The constructor for a FTPFileEntryParserImpl object. */ public FTPFileEntryParserImpl() { } /** * This method is a hook for those implementors (such as VMSVersioningFTPEntryParser, and possibly others) which need to perform some action upon the * FTPFileList after it has been created from the server stream, but before any clients see the list. * * This default implementation does nothing. * * @param original Original list after it has been created from the server stream * * @return original unmodified. */ @Override public List preParse(final List original) { return original; } /** * Reads the next entry using the supplied BufferedReader object up to whatever delimits one entry from the next. This default implementation simply calls * BufferedReader.readLine(). * * @param reader The BufferedReader object from which entries are to be read. * * @return A string representing the next ftp entry or null if none found. * @throws IOException thrown on any IO Error reading from the reader. */ @Override public String readNextEntry(final BufferedReader reader) throws IOException { return reader.readLine(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPFileFilter.java000066400000000000000000000023171434047722200317470ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.ftp; /** * Performs filtering on {@link FTPFile} instances. * * @since 2.2 */ public interface FTPFileFilter { /** * Checks if an FTPFile entry should be included or not. * * @param file entry to be checked for inclusion. May be {@code null}. * @return {@code true} if the file is to be included, {@code false} otherwise. */ boolean accept(FTPFile file); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPFileFilters.java000066400000000000000000000025711434047722200321340ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.ftp; import java.util.Objects; /** * Implements some simple FTPFileFilter classes. * * @since 2.2 */ public class FTPFileFilters { /** * Accepts all FTPFile entries, including null. */ public static final FTPFileFilter ALL = file -> true; /** * Accepts all non-null FTPFile entries. */ public static final FTPFileFilter NON_NULL = Objects::nonNull; /** * Accepts all (non-null) FTPFile directory entries. */ public static final FTPFileFilter DIRECTORIES = file -> file != null && file.isDirectory(); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPHTTPClient.java000066400000000000000000000211071434047722200316360ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.Inet6Address; import java.net.Socket; import java.net.SocketException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.apache.commons.net.util.Base64; /** * Experimental attempt at FTP client that tunnels over an HTTP proxy connection. * * @since 2.2 */ public class FTPHTTPClient extends FTPClient { private static final byte[] CRLF = { '\r', '\n' }; private final String proxyHost; private final int proxyPort; private final String proxyUsername; private final String proxyPassword; private final Charset charset; private final Base64 base64 = new Base64(); private String tunnelHost; // Save the host when setting up a tunnel (needed for EPSV) /** * Create an instance using the UTF-8 encoding, with no proxy credentials. * * @param proxyHost the hostname to use * @param proxyPort the port to use */ public FTPHTTPClient(final String proxyHost, final int proxyPort) { this(proxyHost, proxyPort, null, null); } /** * Create an instance using the specified encoding, with no proxy credentials. * * @param proxyHost the hostname to use * @param proxyPort the port to use * @param encoding the encoding to use */ public FTPHTTPClient(final String proxyHost, final int proxyPort, final Charset encoding) { this(proxyHost, proxyPort, null, null, encoding); } /** * Create an instance using the UTF-8 encoding * * @param proxyHost the hostname to use * @param proxyPort the port to use * @param proxyUser the user name for the proxy * @param proxyPass the password for the proxy */ public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass) { this(proxyHost, proxyPort, proxyUser, proxyPass, StandardCharsets.UTF_8); } /** * Create an instance with the specified encoding * * @param proxyHost the hostname to use * @param proxyPort the port to use * @param proxyUser the user name for the proxy * @param proxyPass the password for the proxy * @param encoding the encoding to use */ public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass, final Charset encoding) { this.proxyHost = proxyHost; this.proxyPort = proxyPort; this.proxyUsername = proxyUser; this.proxyPassword = proxyPass; this.tunnelHost = null; this.charset = encoding; } /** * {@inheritDoc} * * @throws IllegalStateException if connection mode is not passive * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead */ // Kept to maintain binary compatibility // Not strictly necessary, but Clirr complains even though there is a super-impl @Override @Deprecated protected Socket _openDataConnection_(final int command, final String arg) throws IOException { return super._openDataConnection_(command, arg); } /** * {@inheritDoc} * * @throws IllegalStateException if connection mode is not passive * @since 3.1 */ @Override protected Socket _openDataConnection_(final String command, final String arg) throws IOException { // Force local passive mode, active mode not supported by through proxy if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { throw new IllegalStateException("Only passive connection mode supported"); } final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; String passiveHost = null; final boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { _parseExtendedPassiveModeReply(_replyLines.get(0)); passiveHost = this.tunnelHost; } else { if (isInet6Address) { return null; // Must use EPSV for IPV6 } // If EPSV failed on IPV4, revert to PASV if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { return null; } _parsePassiveModeReply(_replyLines.get(0)); passiveHost = this.getPassiveHost(); } final Socket socket = _socketFactory_.createSocket(proxyHost, proxyPort); final InputStream is = socket.getInputStream(); final OutputStream os = socket.getOutputStream(); tunnelHandshake(passiveHost, this.getPassivePort(), is, os); if (getRestartOffset() > 0 && !restart(getRestartOffset())) { socket.close(); return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { socket.close(); return null; } return socket; } @Override public void connect(final String host, final int port) throws SocketException, IOException { _socket_ = _socketFactory_.createSocket(proxyHost, proxyPort); _input_ = _socket_.getInputStream(); _output_ = _socket_.getOutputStream(); final Reader socketIsReader; try { socketIsReader = tunnelHandshake(host, port, _input_, _output_); } catch (final Exception e) { final IOException ioe = new IOException("Could not connect to " + host + " using port " + port); ioe.initCause(e); throw ioe; } super._connectAction_(socketIsReader); } private BufferedReader tunnelHandshake(final String host, final int port, final InputStream input, final OutputStream output) throws IOException, UnsupportedEncodingException { final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1"; final String hostString = "Host: " + host + ":" + port; this.tunnelHost = host; output.write(connectString.getBytes(charset)); output.write(CRLF); output.write(hostString.getBytes(charset)); output.write(CRLF); if (proxyUsername != null && proxyPassword != null) { final String auth = proxyUsername + ":" + proxyPassword; final String header = "Proxy-Authorization: Basic " + base64.encodeToString(auth.getBytes(charset)); output.write(header.getBytes(charset)); } output.write(CRLF); final List response = new ArrayList<>(); final BufferedReader reader = new BufferedReader(new InputStreamReader(input, getCharset())); for (String line = reader.readLine(); line != null && !line.isEmpty(); line = reader.readLine()) { response.add(line); } final int size = response.size(); if (size == 0) { throw new IOException("No response from proxy"); } String code = null; final String resp = response.get(0); if (!resp.startsWith("HTTP/") || (resp.length() < 12)) { throw new IOException("Invalid response from proxy: " + resp); } code = resp.substring(9, 12); if (!"200".equals(code)) { final StringBuilder msg = new StringBuilder(); msg.append("HTTPTunnelConnector: connection failed\r\n"); msg.append("Response received from the proxy:\r\n"); for (final String line : response) { msg.append(line); msg.append("\r\n"); } throw new IOException(msg.toString()); } return reader; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java000066400000000000000000000321711434047722200327570ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.stream.Collectors; import org.apache.commons.net.util.Charsets; /** * This class handles the entire process of parsing a listing of file entries from the server. *

* This object defines a two-part parsing mechanism. *

* The first part is comprised of reading the raw input into an internal list of strings. Every item in this list corresponds to an actual file. All extraneous * matter emitted by the server will have been removed by the end of this phase. This is accomplished in conjunction with the FTPFileEntryParser associated with * this engine, by calling its methods readNextEntry() - which handles the issue of what delimits one entry from another, usually but not always a * line feed and preParse() - which handles removal of extraneous matter such as the preliminary lines of a listing, removal of duplicates on * versioning systems, etc. *

* The second part is composed of the actual parsing, again in conjunction with the particular parser used by this engine. This is controlled by an iterator * over the internal list of strings. This may be done either in block mode, by calling the getNext() and getPrevious() methods to * provide "paged" output of less than the whole list at one time, or by calling the getFiles() method to return the entire list. *

* Examples: *

* Paged access: * *

 * FTPClient f = FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPListParseEngine engine = f.initiateListParsing(directory);
 *
 * while (engine.hasNext()) {
 *     FTPFile[] files = engine.getNext(25); // "page size" you want
 *     // do whatever you want with these files, display them, etc.
 *     // expensive FTPFile objects not created until needed.
 * }
 * 
*

* For unpaged access, simply use FTPClient.listFiles(). That method uses this class transparently. */ public class FTPListParseEngine { /** * An empty immutable {@code FTPFile} array. */ private static final FTPFile[] EMPTY_FTP_FILE_ARRAY = {}; private List entries = new LinkedList<>(); private ListIterator internalIterator = entries.listIterator(); private final FTPFileEntryParser parser; // Should invalid files (parse failures) be allowed? private final boolean saveUnparseableEntries; public FTPListParseEngine(final FTPFileEntryParser parser) { this(parser, null); } /** * Intended for use by FTPClient only * * @since 3.4 */ FTPListParseEngine(final FTPFileEntryParser parser, final FTPClientConfig configuration) { this.parser = parser; if (configuration != null) { this.saveUnparseableEntries = configuration.getUnparseableEntries(); } else { this.saveUnparseableEntries = false; } } /** * Returns a list of FTPFile objects containing the whole list of files returned by the server as read by this object's parser. The files are filtered * before being added to the array. * * @param filter FTPFileFilter, must not be null. * * @return a list of FTPFile objects containing the whole list of files returned by the server as read by this object's parser. *

* NOTE: This array may contain null members if any of the individual file listings failed to parse. The caller should check each entry for * null before referencing it, or use the a filter such as {@link FTPFileFilters#NON_NULL} which does not allow null entries. * @since 3.9.0 */ public List getFileList(final FTPFileFilter filter) { return entries.stream().map(e -> { final FTPFile file = parser.parseFTPEntry(e); return file == null && saveUnparseableEntries ? new FTPFile(e) : file; }).filter(file -> filter.accept(file)).collect(Collectors.toList()); } /** * Returns an array of FTPFile objects containing the whole list of files returned by the server as read by this object's parser. * * @return an array of FTPFile objects containing the whole list of files returned by the server as read by this object's parser. None of the entries will * be null * @throws IOException - not ever thrown, may be removed in a later release */ public FTPFile[] getFiles() throws IOException // TODO remove; not actually thrown { return getFiles(FTPFileFilters.NON_NULL); } /** * Returns an array of FTPFile objects containing the whole list of files returned by the server as read by this object's parser. The files are filtered * before being added to the array. * * @param filter FTPFileFilter, must not be null. * * @return an array of FTPFile objects containing the whole list of files returned by the server as read by this object's parser. *

* NOTE: This array may contain null members if any of the individual file listings failed to parse. The caller should check each entry for * null before referencing it, or use the a filter such as {@link FTPFileFilters#NON_NULL} which does not allow null entries. * @since 2.2 * @throws IOException - not ever thrown, may be removed in a later release */ public FTPFile[] getFiles(final FTPFileFilter filter) throws IOException // TODO remove; not actually thrown { return getFileList(filter).toArray(EMPTY_FTP_FILE_ARRAY); } /** * Returns an array of at most quantityRequested FTPFile objects starting at this object's internal iterator's current position. If fewer than * quantityRequested such elements are available, the returned array will have a length equal to the number of entries at and after after the * current position. If no such entries are found, this array will have a length of 0. * * After this method is called this object's internal iterator is advanced by a number of positions equal to the size of the array returned. * * @param quantityRequested the maximum number of entries we want to get. * * @return an array of at most quantityRequested FTPFile objects starting at the current position of this iterator within its list and at least * the number of elements which exist in the list at and after its current position. *

* NOTE: This array may contain null members if any of the individual file listings failed to parse. The caller should check each entry for * null before referencing it. */ public FTPFile[] getNext(final int quantityRequested) { final List tmpResults = new LinkedList<>(); int count = quantityRequested; while (count > 0 && this.internalIterator.hasNext()) { final String entry = this.internalIterator.next(); FTPFile temp = this.parser.parseFTPEntry(entry); if (temp == null && saveUnparseableEntries) { temp = new FTPFile(entry); } tmpResults.add(temp); count--; } return tmpResults.toArray(EMPTY_FTP_FILE_ARRAY); } /** * Returns an array of at most quantityRequested FTPFile objects starting at this object's internal iterator's current position, and working * back toward the beginning. * * If fewer than quantityRequested such elements are available, the returned array will have a length equal to the number of entries at and * after after the current position. If no such entries are found, this array will have a length of 0. * * After this method is called this object's internal iterator is moved back by a number of positions equal to the size of the array returned. * * @param quantityRequested the maximum number of entries we want to get. * * @return an array of at most quantityRequested FTPFile objects starting at the current position of this iterator within its list and at least * the number of elements which exist in the list at and after its current position. This array will be in the same order as the underlying list * (not reversed). *

* NOTE: This array may contain null members if any of the individual file listings failed to parse. The caller should check each entry for * null before referencing it. */ public FTPFile[] getPrevious(final int quantityRequested) { final List tmpResults = new LinkedList<>(); int count = quantityRequested; while (count > 0 && this.internalIterator.hasPrevious()) { final String entry = this.internalIterator.previous(); FTPFile temp = this.parser.parseFTPEntry(entry); if (temp == null && saveUnparseableEntries) { temp = new FTPFile(entry); } tmpResults.add(0, temp); count--; } return tmpResults.toArray(EMPTY_FTP_FILE_ARRAY); } /** * convenience method to allow clients to know whether this object's internal iterator's current position is at the end of the list. * * @return true if internal iterator is not at end of list, false otherwise. */ public boolean hasNext() { return internalIterator.hasNext(); } /** * convenience method to allow clients to know whether this object's internal iterator's current position is at the beginning of the list. * * @return true if internal iterator is not at beginning of list, false otherwise. */ public boolean hasPrevious() { return internalIterator.hasPrevious(); } /** * Internal method for reading (and closing) the input into the entries list. After this method has completed, entries will * contain a collection of entries (as defined by FTPFileEntryParser.readNextEntry()), but this may contain various non-entry preliminary lines * from the server output, duplicates, and other data that will not be part of the final listing. * * @param inputStream The socket stream on which the input will be read. * @param charsetName The encoding to use. * * @throws IOException thrown on any failure to read the stream */ private void read(final InputStream inputStream, final String charsetName) throws IOException { try (final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charsets.toCharset(charsetName)))) { String line = this.parser.readNextEntry(reader); while (line != null) { this.entries.add(line); line = this.parser.readNextEntry(reader); } } } /** * Do not use. * * @param inputStream the stream from which to read * @throws IOException on error * @deprecated use {@link #readServerList(InputStream, String)} instead */ @Deprecated public void readServerList(final InputStream inputStream) throws IOException { readServerList(inputStream, null); } /** * Reads (and closes) the initial reading and preparsing of the list returned by the server. After this method has completed, this object will contain a * list of unparsed entries (Strings) each referring to a unique file on the server. * * @param inputStream input stream provided by the server socket. * @param charsetName the encoding to be used for reading the stream * * @throws IOException thrown on any failure to read from the sever. */ public void readServerList(final InputStream inputStream, final String charsetName) throws IOException { this.entries = new LinkedList<>(); read(inputStream, charsetName); this.parser.preParse(this.entries); resetIterator(); } // DEPRECATED METHODS - for API compatibility only - DO NOT USE /** * resets this object's internal iterator to the beginning of the list. */ public void resetIterator() { this.internalIterator = this.entries.listIterator(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPReply.java000066400000000000000000000172211434047722200310150ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; /** * FTPReply stores a set of constants for FTP reply codes. To interpret the meaning of the codes, familiarity with RFC 959 is assumed. The mnemonic constant * names are transcriptions from the code descriptions of RFC 959. *

* TODO replace with an enum */ public final class FTPReply { public static final int RESTART_MARKER = 110; public static final int SERVICE_NOT_READY = 120; public static final int DATA_CONNECTION_ALREADY_OPEN = 125; public static final int FILE_STATUS_OK = 150; public static final int COMMAND_OK = 200; public static final int COMMAND_IS_SUPERFLUOUS = 202; public static final int SYSTEM_STATUS = 211; public static final int DIRECTORY_STATUS = 212; public static final int FILE_STATUS = 213; public static final int HELP_MESSAGE = 214; public static final int NAME_SYSTEM_TYPE = 215; public static final int SERVICE_READY = 220; public static final int SERVICE_CLOSING_CONTROL_CONNECTION = 221; public static final int DATA_CONNECTION_OPEN = 225; public static final int CLOSING_DATA_CONNECTION = 226; public static final int ENTERING_PASSIVE_MODE = 227; /** @since 2.2 */ public static final int ENTERING_EPSV_MODE = 229; public static final int USER_LOGGED_IN = 230; public static final int FILE_ACTION_OK = 250; public static final int PATHNAME_CREATED = 257; public static final int NEED_PASSWORD = 331; public static final int NEED_ACCOUNT = 332; public static final int FILE_ACTION_PENDING = 350; public static final int SERVICE_NOT_AVAILABLE = 421; public static final int CANNOT_OPEN_DATA_CONNECTION = 425; public static final int TRANSFER_ABORTED = 426; public static final int FILE_ACTION_NOT_TAKEN = 450; public static final int ACTION_ABORTED = 451; public static final int INSUFFICIENT_STORAGE = 452; public static final int UNRECOGNIZED_COMMAND = 500; public static final int SYNTAX_ERROR_IN_ARGUMENTS = 501; public static final int COMMAND_NOT_IMPLEMENTED = 502; public static final int BAD_COMMAND_SEQUENCE = 503; public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504; public static final int NOT_LOGGED_IN = 530; public static final int NEED_ACCOUNT_FOR_STORING_FILES = 532; public static final int FILE_UNAVAILABLE = 550; public static final int PAGE_TYPE_UNKNOWN = 551; public static final int STORAGE_ALLOCATION_EXCEEDED = 552; public static final int FILE_NAME_NOT_ALLOWED = 553; // FTPS Reply Codes /** @since 2.0 */ public static final int SECURITY_DATA_EXCHANGE_COMPLETE = 234; /** @since 2.0 */ public static final int SECURITY_DATA_EXCHANGE_SUCCESSFULLY = 235; /** @since 2.0 */ public static final int SECURITY_MECHANISM_IS_OK = 334; /** @since 2.0 */ public static final int SECURITY_DATA_IS_ACCEPTABLE = 335; /** @since 2.0 */ public static final int UNAVAILABLE_RESOURCE = 431; /** @since 2.2 */ public static final int BAD_TLS_NEGOTIATION_OR_DATA_ENCRYPTION_REQUIRED = 522; /** @since 2.0 */ public static final int DENIED_FOR_POLICY_REASONS = 533; /** @since 2.0 */ public static final int REQUEST_DENIED = 534; /** @since 2.0 */ public static final int FAILED_SECURITY_CHECK = 535; /** @since 2.0 */ public static final int REQUESTED_PROT_LEVEL_NOT_SUPPORTED = 536; // IPv6 error codes // Note this is also used as an FTPS error code reply /** @since 2.2 */ public static final int EXTENDED_PORT_FAILURE = 522; /** * Determine if a reply code is a negative permanent response. All codes beginning with a 5 are negative permanent responses. The FTP server will send a * negative permanent response on the failure of a command that cannot be reattempted with success. * * @param reply The reply code to test. * @return True if a reply code is a negative permanent response, false if not. */ public static boolean isNegativePermanent(final int reply) { return reply >= 500 && reply < 600; } /** * Determine if a reply code is a negative transient response. All codes beginning with a 4 are negative transient responses. The FTP server will send a * negative transient response on the failure of a command that can be reattempted with success. * * @param reply The reply code to test. * @return True if a reply code is a negative transient response, false if not. */ public static boolean isNegativeTransient(final int reply) { return reply >= 400 && reply < 500; } /** * Determine if a reply code is a positive completion response. All codes beginning with a 2 are positive completion responses. The FTP server will send a * positive completion response on the final successful completion of a command. * * @param reply The reply code to test. * @return True if a reply code is a positive completion response, false if not. */ public static boolean isPositiveCompletion(final int reply) { return reply >= 200 && reply < 300; } /** * Determine if a reply code is a positive intermediate response. All codes beginning with a 3 are positive intermediate responses. The FTP server will send * a positive intermediate response on the successful completion of one part of a multi-part sequence of commands. For example, after a successful USER * command, a positive intermediate response will be sent to indicate that the server is ready for the PASS command. * * @param reply The reply code to test. * @return True if a reply code is a positive intermediate response, false if not. */ public static boolean isPositiveIntermediate(final int reply) { return reply >= 300 && reply < 400; } /** * Determine if a reply code is a positive preliminary response. All codes beginning with a 1 are positive preliminary responses. Postitive preliminary * responses are used to indicate tentative success. No further commands can be issued to the FTP server after a positive preliminary response until a * follow up response is received from the server. * * @param reply The reply code to test. * @return True if a reply code is a positive preliminary response, false if not. */ public static boolean isPositivePreliminary(final int reply) { return reply >= 100 && reply < 200; } /** * Determine if a reply code is a protected response. * * @param reply The reply code to test. * @return True if a reply code is a protected response, false if not. * @since 3.0 */ public static boolean isProtectedReplyCode(final int reply) { // actually, only 3 protected reply codes are // defined in RFC 2228: 631, 632 and 633. return reply >= 600 && reply < 700; } // Cannot be instantiated private FTPReply() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPSClient.java000066400000000000000000001173241434047722200312700ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.apache.commons.net.util.Base64; import org.apache.commons.net.util.SSLContextUtils; import org.apache.commons.net.util.SSLSocketUtils; import org.apache.commons.net.util.TrustManagerUtils; /** * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to see wire-level SSL details. * * Warning: the hostname is not verified against the certificate by default, use {@link #setHostnameVerifier(HostnameVerifier)} or * {@link #setEndpointCheckingEnabled(boolean)} (on Java 1.7+) to enable verification. Verification is only performed on client mode connections. * * @since 2.0 */ public class FTPSClient extends FTPClient { // From http://www.iana.org/assignments/port-numbers // ftps-data 989/tcp ftp protocol, data, over TLS/SSL // ftps-data 989/udp ftp protocol, data, over TLS/SSL // ftps 990/tcp ftp protocol, control, over TLS/SSL // ftps 990/udp ftp protocol, control, over TLS/SSL public static final int DEFAULT_FTPS_DATA_PORT = 989; public static final int DEFAULT_FTPS_PORT = 990; /** The value that I can set in PROT command (C = Clear, P = Protected) */ private static final String[] PROT_COMMAND_VALUE = { "C", "E", "S", "P" }; /** Default PROT Command */ private static final String DEFAULT_PROT = "C"; /** Default secure socket protocol name, i.e. TLS */ private static final String DEFAULT_PROTOCOL = "TLS"; /** The AUTH (Authentication/Security Mechanism) command. */ private static final String CMD_AUTH = "AUTH"; /** The ADAT (Authentication/Security Data) command. */ private static final String CMD_ADAT = "ADAT"; /** The PROT (Data Channel Protection Level) command. */ private static final String CMD_PROT = "PROT"; /** The PBSZ (Protection Buffer Size) command. */ private static final String CMD_PBSZ = "PBSZ"; /** The MIC (Integrity Protected Command) command. */ private static final String CMD_MIC = "MIC"; /** The CONF (Confidentiality Protected Command) command. */ private static final String CMD_CONF = "CONF"; /** The ENC (Privacy Protected Command) command. */ private static final String CMD_ENC = "ENC"; /** The CCC (Clear Command Channel) command. */ private static final String CMD_CCC = "CCC"; /** @deprecated - not used - may be removed in a future release */ @Deprecated public static String KEYSTORE_ALGORITHM; /** @deprecated - not used - may be removed in a future release */ @Deprecated public static String TRUSTSTORE_ALGORITHM; /** @deprecated - not used - may be removed in a future release */ @Deprecated public static String PROVIDER; /** @deprecated - not used - may be removed in a future release */ @Deprecated public static String STORE_TYPE; /** The security mode. (True - Implicit Mode / False - Explicit Mode) */ private final boolean isImplicit; /** The secure socket protocol to be used, e.g. SSL/TLS. */ private final String protocol; /** The AUTH Command value */ private String auth = DEFAULT_PROTOCOL; /** The context object. */ private SSLContext context; /** The socket object. */ private Socket plainSocket; /** Controls whether a new SSL session may be established by this socket. Default true. */ private boolean isCreation = true; /** The use client mode flag. */ private boolean isClientMode = true; /** The need client auth flag. */ private boolean isNeedClientAuth; /** The want client auth flag. */ private boolean isWantClientAuth; /** The cipher suites */ private String[] suites; /** The protocol versions */ private String[] protocols; /** * The FTPS {@link TrustManager} implementation, default validate only {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}. */ private TrustManager trustManager = TrustManagerUtils.getValidateServerCertificateTrustManager(); /** The {@link KeyManager}, default null (i.e. use system default). */ private KeyManager keyManager; /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ private HostnameVerifier hostnameVerifier; /** Use Java 1.7+ HTTPS Endpoint Identification Algorithm. */ private boolean tlsEndpointChecking; /** * Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}. * * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit (isImplicit = false) */ public FTPSClient() { this(DEFAULT_PROTOCOL, false); } /** * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS Calls {@link #FTPSClient(String, boolean)} * * @param isImplicit The security mode (Implicit/Explicit). */ public FTPSClient(final boolean isImplicit) { this(DEFAULT_PROTOCOL, isImplicit); } /** * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS The default TrustManager is set from * {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} * * @param isImplicit The security mode(Implicit/Explicit). * @param context A pre-configured SSL Context */ public FTPSClient(final boolean isImplicit, final SSLContext context) { this(DEFAULT_PROTOCOL, isImplicit); this.context = context; } /** * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS and isImplicit {@code false} Calls {@link #FTPSClient(boolean, SSLContext)} * * @param context A pre-configured SSL Context */ public FTPSClient(final SSLContext context) { this(false, context); } /** * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}. * * @param protocol the protocol to use */ public FTPSClient(final String protocol) { this(protocol, false); } /** * Constructor for FTPSClient allowing specification of protocol and security mode. If isImplicit is true, the port is set to {@link #DEFAULT_FTPS_PORT} * i.e. 990. The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} * * @param protocol the protocol * @param isImplicit The security mode(Implicit/Explicit). */ public FTPSClient(final String protocol, final boolean isImplicit) { this.protocol = protocol; this.isImplicit = isImplicit; if (isImplicit) { setDefaultPort(DEFAULT_FTPS_PORT); } } /** * Because there are so many connect() methods, the _connectAction_() method is provided as a means of performing some action immediately after establishing * a connection, rather than reimplementing all of the connect() methods. * * @throws IOException If it throw by _connectAction_. * @see org.apache.commons.net.SocketClient#_connectAction_() */ @Override protected void _connectAction_() throws IOException { // Implicit mode. if (isImplicit) { applySocketAttributes(); sslNegotiation(); } super._connectAction_(); // Explicit mode. if (!isImplicit) { execAUTH(); sslNegotiation(); } } /** * Returns a socket of the data connection. Wrapped as an {@link SSLSocket}, which carries out handshake processing. * * @param command The int representation of the FTP command to send. * @param arg The arguments to the FTP command. If this parameter is set to null, then the command is sent with no arguments. * @return corresponding to the established data connection. Null is returned if an FTP protocol error is reported at any point during the establishment and * initialization of the connection. * @throws IOException If there is any problem with the connection. * @see FTPClient#_openDataConnection_(int, String) * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead */ @Override // Strictly speaking this is not needed, but it works round a Clirr bug // So rather than invoke the parent code, we do it here @Deprecated protected Socket _openDataConnection_(final int command, final String arg) throws IOException { return _openDataConnection_(FTPCommand.getCommand(command), arg); } /** * Returns a socket of the data connection. Wrapped as an {@link SSLSocket}, which carries out handshake processing. * * @param command The textual representation of the FTP command to send. * @param arg The arguments to the FTP command. If this parameter is set to null, then the command is sent with no arguments. * @return corresponding to the established data connection. Null is returned if an FTP protocol error is reported at any point during the establishment and * initialization of the connection. * @throws IOException If there is any problem with the connection. * @see FTPClient#_openDataConnection_(int, String) * @since 3.2 */ @Override protected Socket _openDataConnection_(final String command, final String arg) throws IOException { final Socket socket = openDataSecureConnection(command, arg); _prepareDataSocket_(socket); if (socket instanceof SSLSocket) { final SSLSocket sslSocket = (SSLSocket) socket; sslSocket.setUseClientMode(isClientMode); sslSocket.setEnableSessionCreation(isCreation); // server mode if (!isClientMode) { sslSocket.setNeedClientAuth(isNeedClientAuth); sslSocket.setWantClientAuth(isWantClientAuth); } if (suites != null) { sslSocket.setEnabledCipherSuites(suites); } if (protocols != null) { sslSocket.setEnabledProtocols(protocols); } sslSocket.startHandshake(); } return socket; } /** * Performs any custom initialization for a newly created SSLSocket (before the SSL handshake happens). Called by {@link #_openDataConnection_(int, String)} * immediately after creating the socket. The default implementation is a no-op * * @param socket the socket to set up * @throws IOException on error * @since 3.1 */ protected void _prepareDataSocket_(final Socket socket) throws IOException { } /** * Check the value that can be set in PROT Command value. * * @param prot Data Channel Protection Level. * @return True - A set point is right / False - A set point is not right */ private boolean checkPROTValue(final String prot) { for (final String element : PROT_COMMAND_VALUE) { if (element.equals(prot)) { return true; } } return false; } /** * Close open sockets. * * @param socket main socket for proxy if enabled * @param sslSocket ssl socket * @throws IOException closing sockets is not successful */ private void closeSockets(final Socket socket, final Socket sslSocket) throws IOException { if (socket != null) { socket.close(); } if (sslSocket != null) { sslSocket.close(); } } /** * Create SSL socket from plain socket. * * @param socket * @return SSL Socket * @throws IOException */ private SSLSocket createSSLSocket(final Socket socket) throws IOException { if (socket != null) { final SSLSocketFactory f = context.getSocketFactory(); return (SSLSocket) f.createSocket(socket, _hostname_, socket.getPort(), false); } return null; } /** * Closes the connection to the FTP server and restores connection parameters to the default values. *

* Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)} to reset the factories that may have been changed during the session, e.g. * by {@link #execPROT(String)} * * @throws IOException If an error occurs while disconnecting. * @since 3.0 */ @Override public void disconnect() throws IOException { super.disconnect(); if (plainSocket != null) { plainSocket.close(); } setSocketFactory(null); setServerSocketFactory(null); } /** * Send the ADAT command with the specified authentication data. * * @param data The data to send with the command. * @return server reply. * @throws IOException If an I/O error occurs while sending the command. * @since 3.0 */ public int execADAT(final byte[] data) throws IOException { if (data != null) { return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data)); } return sendCommand(CMD_ADAT); } /** * AUTH command. * * @throws SSLException If it server reply code not equal "234" and "334". * @throws IOException If an I/O error occurs while either sending the command. */ protected void execAUTH() throws SSLException, IOException { final int replyCode = sendCommand(CMD_AUTH, auth); if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) { // replyCode = 334 // I carry out an ADAT command. } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) { throw new SSLException(getReplyString()); } } /** * Send the AUTH command with the specified mechanism. * * @param mechanism The mechanism name to send with the command. * @return server reply. * @throws IOException If an I/O error occurs while sending the command. * @since 3.0 */ public int execAUTH(final String mechanism) throws IOException { return sendCommand(CMD_AUTH, mechanism); } /** * Send the CCC command to the server. The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance to be assigned to a plain * {@link Socket} instances * * @return server reply. * @throws IOException If an I/O error occurs while sending the command. * @since 3.0 */ public int execCCC() throws IOException { final int repCode = sendCommand(CMD_CCC); // This will be performed by sendCommand(String, String) // if (FTPReply.isPositiveCompletion(repCode)) { // _socket_.close(); // _socket_ = plainSocket; // _controlInput_ = new BufferedReader( // new InputStreamReader( // _socket_.getInputStream(), getControlEncoding())); // _controlOutput_ = new BufferedWriter( // new OutputStreamWriter( // _socket_.getOutputStream(), getControlEncoding())); // } return repCode; } /** * Send the CONF command with the specified data. * * @param data The data to send with the command. * @return server reply. * @throws IOException If an I/O error occurs while sending the command. * @since 3.0 */ public int execCONF(final byte[] data) throws IOException { if (data != null) { return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data)); } return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)? } /** * Send the ENC command with the specified data. * * @param data The data to send with the command. * @return server reply. * @throws IOException If an I/O error occurs while sending the command. * @since 3.0 */ public int execENC(final byte[] data) throws IOException { if (data != null) { return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data)); } return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)? } /** * Send the MIC command with the specified data. * * @param data The data to send with the command. * @return server reply. * @throws IOException If an I/O error occurs while sending the command. * @since 3.0 */ public int execMIC(final byte[] data) throws IOException { if (data != null) { return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data)); } return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)? } /** * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. * * @param pbsz Protection Buffer Size. * @throws SSLException If the server reply code does not equal "200". * @throws IOException If an I/O error occurs while sending the command. * @see #parsePBSZ(long) */ public void execPBSZ(final long pbsz) throws SSLException, IOException { if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number throw new IllegalArgumentException(); } final int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz)); if (FTPReply.COMMAND_OK != status) { throw new SSLException(getReplyString()); } } /** * PROT command. *

    *
  • C - Clear
  • *
  • S - Safe(SSL protocol only)
  • *
  • E - Confidential(SSL protocol only)
  • *
  • P - Private
  • *
* N.B. the method calls {@link #setSocketFactory(javax.net.SocketFactory)} and {@link #setServerSocketFactory(javax.net.ServerSocketFactory)} * * @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}. * @throws SSLException If the server reply code does not equal {@code 200}. * @throws IOException If an I/O error occurs while sending the command. */ public void execPROT(String prot) throws SSLException, IOException { if (prot == null) { prot = DEFAULT_PROT; } if (!checkPROTValue(prot)) { throw new IllegalArgumentException(); } if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) { throw new SSLException(getReplyString()); } if (DEFAULT_PROT.equals(prot)) { setSocketFactory(null); setServerSocketFactory(null); } else { setSocketFactory(new FTPSSocketFactory(context)); setServerSocketFactory(new FTPSServerSocketFactory(context)); initSslContext(); } } /** * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234 * * @param prefix the prefix to find * @param reply where to find the prefix * @return the remainder of the string after the prefix, or null if the prefix was not present. */ private String extractPrefixedData(final String prefix, final String reply) { final int idx = reply.indexOf(prefix); if (idx == -1) { return null; } // N.B. Cannot use trim before substring as leading space would affect the offset. return reply.substring(idx + prefix.length()).trim(); } /** * Return AUTH command use value. * * @return AUTH command use value. */ public String getAuthValue() { return this.auth; } /** * Returns the names of the cipher suites which could be enabled for use on this connection. When the underlying {@link Socket} is not an {@link SSLSocket} * instance, returns null. * * @return An array of cipher suite names, or null */ public String[] getEnabledCipherSuites() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledCipherSuites(); } return null; } /** * Returns the names of the protocol versions which are currently enabled for use on this connection. When the underlying {@link Socket} is not an * {@link SSLSocket} instance, returns null. * * @return An array of protocols, or null */ public String[] getEnabledProtocols() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledProtocols(); } return null; } /** * Returns true if new SSL sessions may be established by this socket. When the underlying {@link Socket} instance is not SSL-enabled (i.e. an instance of * {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled, this returns False. * * @return true - Indicates that sessions may be created; this is the default. false - indicates that an existing session must be resumed. */ public boolean getEnableSessionCreation() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnableSessionCreation(); } return false; } /** * Get the currently configured {@link HostnameVerifier}. The verifier is only used on client mode connections. * * @return A HostnameVerifier instance. * @since 3.4 */ public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } /** * Gets the {@link KeyManager} instance. * * @return The {@link KeyManager} instance */ private KeyManager getKeyManager() { return keyManager; } /** * Returns true if the socket will require client authentication. When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. * * @return true - If the server mode socket should request that the client authenticate itself. */ public boolean getNeedClientAuth() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getNeedClientAuth(); } return false; } /** * Get the currently configured {@link TrustManager}. * * @return A TrustManager instance. */ public TrustManager getTrustManager() { return trustManager; } /** * Returns true if the socket is set to use client mode in its first handshake. When the underlying {@link Socket} is not an {@link SSLSocket} instance, * returns false. * * @return true - If the socket should start its first handshake in "client" mode. */ public boolean getUseClientMode() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getUseClientMode(); } return false; } /** * Returns true if the socket will request client authentication. When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. * * @return true - If the server mode socket should request that the client authenticate itself. */ public boolean getWantClientAuth() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getWantClientAuth(); } return false; } /** * Performs a lazy init of the SSL context * * @throws IOException */ private void initSslContext() throws IOException { if (context == null) { context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); } } /** * Return whether or not endpoint identification using the HTTPS algorithm on Java 1.7+ is enabled. The default behavior is for this to be disabled. * * This check is only performed on client mode connections. * * @return True if enabled, false if not. * @since 3.4 */ public boolean isEndpointCheckingEnabled() { return tlsEndpointChecking; } /** * Establishes a data connection with the FTP server, returning a Socket for the connection if successful. If a restart offset has been set with * {@link #setRestartOffset(long)}, a REST command is issued to the server with the offset as an argument before establishing the data connection. Active * mode connections also cause a local PORT command to be issued. * * @param command The text representation of the FTP command to send. * @param arg The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument. * @return A Socket corresponding to the established data connection. Null is returned if an FTP protocol error is reported at any point during the * establishment and initialization of the connection. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.1 */ private Socket openDataSecureConnection(final String command, final String arg) throws IOException { if (getDataConnectionMode() != ACTIVE_LOCAL_DATA_CONNECTION_MODE && getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { return null; } final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; final Socket socket; Socket sslSocket = null; final int soTimeoutMillis = DurationUtils.toMillisInt(getDataTimeout()); if (getDataConnectionMode() == ACTIVE_LOCAL_DATA_CONNECTION_MODE) { // if no activePortRange was set (correctly) -> getActivePort() = 0 // -> new ServerSocket(0) -> bind to any free local port try (final ServerSocket server = _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress())) { // Try EPRT only if remote server is over IPv6, if not use PORT, // because EPRT has no advantage over PORT on IPv4. // It could even have the disadvantage, // that EPRT will make the data connection fail, because // today's intelligent NAT Firewalls are able to // substitute IP addresses in the PORT command, // but might not be able to recognize the EPRT command. if (isInet6Address) { if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))) { return null; } } else if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))) { return null; } if ((getRestartOffset() > 0) && !restart(getRestartOffset())) { return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { return null; } // For now, let's just use the data timeout value for waiting for // the data connection. It may be desirable to let this be a // separately configurable value. In any case, we really want // to allow preventing the accept from blocking indefinitely. if (soTimeoutMillis >= 0) { server.setSoTimeout(soTimeoutMillis); } socket = server.accept(); // Ensure the timeout is set before any commands are issued on the new socket if (soTimeoutMillis >= 0) { socket.setSoTimeout(soTimeoutMillis); } if (getReceiveDataSocketBufferSize() > 0) { socket.setReceiveBufferSize(getReceiveDataSocketBufferSize()); } if (getSendDataSocketBufferSize() > 0) { socket.setSendBufferSize(getSendDataSocketBufferSize()); } } } else { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE // Try EPSV command first on IPv6 - and IPv4 if enabled. // When using IPv4 with NAT it has the advantage // to work with more rare configurations. // E.g. if FTP server has a static PASV address (external network) // and the client is coming from another internal network. // In that case the data connection after PASV command would fail, // while EPSV would make the client succeed by taking just the port. final boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { _parseExtendedPassiveModeReply(_replyLines.get(0)); } else { if (isInet6Address) { return null; // Must use EPSV for IPV6 } // If EPSV failed on IPV4, revert to PASV if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { return null; } _parsePassiveModeReply(_replyLines.get(0)); } if (getProxy() != null) { socket = new Socket(getProxy()); } else { socket = _socketFactory_.createSocket(); } if (getReceiveDataSocketBufferSize() > 0) { socket.setReceiveBufferSize(getReceiveDataSocketBufferSize()); } if (getSendDataSocketBufferSize() > 0) { socket.setSendBufferSize(getSendDataSocketBufferSize()); } if (getPassiveLocalIPAddress() != null) { socket.bind(new InetSocketAddress(getPassiveLocalIPAddress(), 0)); } // For now, let's just use the data timeout value for waiting for // the data connection. It may be desirable to let this be a // separately configurable value. In any case, we really want // to allow preventing the accept from blocking indefinitely. if (soTimeoutMillis >= 0) { socket.setSoTimeout(soTimeoutMillis); } socket.connect(new InetSocketAddress(getPassiveHost(), getPassivePort()), connectTimeout); if (getProxy() != null) { sslSocket = context.getSocketFactory().createSocket(socket, getPassiveHost(), getPassivePort(), true); } if ((getRestartOffset() > 0) && !restart(getRestartOffset())) { closeSockets(socket, sslSocket); return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { closeSockets(socket, sslSocket); return null; } } if (isRemoteVerificationEnabled() && !verifyRemote(socket)) { // Grab the host before we close the socket to avoid NET-663 final InetAddress socketHost = socket.getInetAddress(); closeSockets(socket, sslSocket); throw new IOException( "Host attempting data connection " + socketHost.getHostAddress() + " is not same as server " + getRemoteAddress().getHostAddress()); } return getProxy() != null ? sslSocket : socket; } /** * Parses the given ADAT response line and base64-decodes the data. * * @param reply The ADAT reply to parse. * @return the data in the reply, base64-decoded. * @since 3.0 */ public byte[] parseADATReply(final String reply) { if (reply == null) { return null; } return Base64.decodeBase64(extractPrefixedData("ADAT=", reply)); } /** * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. Issues the command and parses the response to return the negotiated value. * * @param pbsz Protection Buffer Size. * @throws SSLException If the server reply code does not equal "200". * @throws IOException If an I/O error occurs while sending the command. * @return the negotiated value. * @see #execPBSZ(long) * @since 3.0 */ public long parsePBSZ(final long pbsz) throws SSLException, IOException { execPBSZ(pbsz); long minvalue = pbsz; final String remainder = extractPrefixedData("PBSZ=", getReplyString()); if (remainder != null) { final long replysz = Long.parseLong(remainder); if (replysz < minvalue) { minvalue = replysz; } } return minvalue; } /** * Send an FTP command. A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance to be assigned to a plain * {@link Socket} * * @param command The FTP command. * @return server reply. * @throws IOException If an I/O error occurs while sending the command. * @throws SSLException if a CCC command fails * @see org.apache.commons.net.ftp.FTP#sendCommand(String) */ // Would like to remove this method, but that will break any existing clients that are using CCC @Override public int sendCommand(final String command, final String args) throws IOException { final int repCode = super.sendCommand(command, args); /* If CCC is issued, restore socket i/o streams to unsecured versions */ if (CMD_CCC.equals(command)) { if (FTPReply.COMMAND_OK != repCode) { throw new SSLException(getReplyString()); } _socket_.close(); _socket_ = plainSocket; _controlInput_ = new BufferedReader(new InputStreamReader(_socket_.getInputStream(), getControlEncoding())); _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_socket_.getOutputStream(), getControlEncoding())); } return repCode; } /** * Set AUTH command use value. This processing is done before connected processing. * * @param auth AUTH command use value. */ public void setAuthValue(final String auth) { this.auth = auth; } /** * Controls which particular cipher suites are enabled for use on this connection. Called before server negotiation. * * @param cipherSuites The cipher suites. */ public void setEnabledCipherSuites(final String[] cipherSuites) { suites = cipherSuites.clone(); } /** * Controls which particular protocol versions are enabled for use on this connection. I perform setting before a server negotiation. * * @param protocolVersions The protocol versions. */ public void setEnabledProtocols(final String[] protocolVersions) { protocols = protocolVersions.clone(); } /** * Controls whether a new SSL session may be established by this socket. * * @param isCreation The established socket flag. */ public void setEnabledSessionCreation(final boolean isCreation) { this.isCreation = isCreation; } /** * Automatic endpoint identification checking using the HTTPS algorithm is supported on Java 1.7+. The default behavior is for this to be disabled. * * This check is only performed on client mode connections. * * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+. * @since 3.4 */ public void setEndpointCheckingEnabled(final boolean enable) { tlsEndpointChecking = enable; } /** * Override the default {@link HostnameVerifier} to use. The verifier is only used on client mode connections. * * @param newHostnameVerifier The HostnameVerifier implementation to set or null to disable. * @since 3.4 */ public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier) { hostnameVerifier = newHostnameVerifier; } /** * Set a {@link KeyManager} to use * * @param keyManager The KeyManager implementation to set. * @see org.apache.commons.net.util.KeyManagerUtils */ public void setKeyManager(final KeyManager keyManager) { this.keyManager = keyManager; } // DEPRECATED - for API compatibility only - DO NOT USE /** * Configures the socket to require client authentication. * * @param isNeedClientAuth The need client auth flag. */ public void setNeedClientAuth(final boolean isNeedClientAuth) { this.isNeedClientAuth = isNeedClientAuth; } /** * Override the default {@link TrustManager} to use; if set to {@code null}, the default TrustManager from the JVM will be used. * * @param trustManager The TrustManager implementation to set, may be {@code null} * @see org.apache.commons.net.util.TrustManagerUtils */ public void setTrustManager(final TrustManager trustManager) { this.trustManager = trustManager; } /** * Configures the socket to use client (or server) mode in its first handshake. * * @param isClientMode The use client mode flag. */ public void setUseClientMode(final boolean isClientMode) { this.isClientMode = isClientMode; } /** * Configures the socket to request client authentication, but only if such a request is appropriate to the cipher suite negotiated. * * @param isWantClientAuth The want client auth flag. */ public void setWantClientAuth(final boolean isWantClientAuth) { this.isWantClientAuth = isWantClientAuth; } /** * SSL/TLS negotiation. Acquires an SSL socket of a control connection and carries out handshake processing. * * @throws IOException If server negotiation fails */ protected void sslNegotiation() throws IOException { plainSocket = _socket_; initSslContext(); final SSLSocket socket = createSSLSocket(_socket_); socket.setEnableSessionCreation(isCreation); socket.setUseClientMode(isClientMode); // client mode if (isClientMode) { if (tlsEndpointChecking) { SSLSocketUtils.enableEndpointNameVerification(socket); } } else { // server mode socket.setNeedClientAuth(isNeedClientAuth); socket.setWantClientAuth(isWantClientAuth); } if (protocols != null) { socket.setEnabledProtocols(protocols); } if (suites != null) { socket.setEnabledCipherSuites(suites); } socket.startHandshake(); // TODO the following setup appears to duplicate that in the super class methods _socket_ = socket; _controlInput_ = new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding())); _controlOutput_ = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), getControlEncoding())); if (isClientMode && (hostnameVerifier != null && !hostnameVerifier.verify(_hostname_, socket.getSession()))) { throw new SSLHandshakeException("Hostname doesn't match certificate"); } } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPSCommand.java000066400000000000000000000035441434047722200314260ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; /** * FTPS-specific commands. * * @since 2.0 * @deprecated 3.0 DO NOT USE */ @Deprecated public final class FTPSCommand { public static final int AUTH = 0; public static final int ADAT = 1; public static final int PBSZ = 2; public static final int PROT = 3; public static final int CCC = 4; public static final int AUTHENTICATION_SECURITY_MECHANISM = AUTH; public static final int AUTHENTICATION_SECURITY_DATA = ADAT; public static final int PROTECTION_BUFFER_SIZE = PBSZ; public static final int DATA_CHANNEL_PROTECTION_LEVEL = PROT; public static final int CLEAR_COMMAND_CHANNEL = CCC; private static final String[] commands = { "AUTH", "ADAT", "PBSZ", "PROT", "CCC" }; /** * Retrieve the FTPS command string corresponding to a specified command code. * * @param command The command code. * @return The FTPS command string corresponding to a specified command code. */ public static String getCommand(final int command) { return commands[command]; } } FTPSServerSocketFactory.java000066400000000000000000000057761434047722200337510ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; /** * Server socket factory for FTPS connections. * * @since 2.2 */ public class FTPSServerSocketFactory extends ServerSocketFactory { /** Factory for secure socket factories */ private final SSLContext sslContext; /** * Constructs a new instance for the given SSL context. * * @param sslContext The SSL context. */ public FTPSServerSocketFactory(final SSLContext sslContext) { this.sslContext = sslContext; } @SuppressWarnings("resource") // Factory method. @Override public ServerSocket createServerSocket() throws IOException { return init(getServerSocketFactory().createServerSocket()); } @SuppressWarnings("resource") // Factory method. @Override public ServerSocket createServerSocket(final int port) throws IOException { return init(getServerSocketFactory().createServerSocket(port)); } @SuppressWarnings("resource") // Factory method. @Override public ServerSocket createServerSocket(final int port, final int backlog) throws IOException { return init(getServerSocketFactory().createServerSocket(port, backlog)); } @SuppressWarnings("resource") // Factory method. @Override public ServerSocket createServerSocket(final int port, final int backlog, final InetAddress ifAddress) throws IOException { return init(getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); } private SSLServerSocketFactory getServerSocketFactory() { return this.sslContext.getServerSocketFactory(); } /** * Sets the socket so newly accepted connections will use SSL client mode. * * @param socket the SSLServerSocket to initialize * @return the socket * @throws ClassCastException if socket is not an instance of SSLServerSocket */ public ServerSocket init(final ServerSocket socket) { ((SSLServerSocket) socket).setUseClientMode(true); return socket; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java000066400000000000000000000104071434047722200326240ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; /** * * Socket factory for FTPS connections. * * @since 2.0 */ public class FTPSSocketFactory extends SocketFactory { private final SSLContext context; public FTPSSocketFactory(final SSLContext context) { this.context = context; } /** * @param port the port * @return the socket * @throws IOException on error * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int) instead} */ @Deprecated public java.net.ServerSocket createServerSocket(final int port) throws IOException { return this.init(this.context.getServerSocketFactory().createServerSocket(port)); } /** * @param port the port * @param backlog the backlog * @return the socket * @throws IOException on error * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int) instead} */ @Deprecated public java.net.ServerSocket createServerSocket(final int port, final int backlog) throws IOException { return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); } /** * @param port the port * @param backlog the backlog * @param ifAddress the interface * @return the socket * @throws IOException on error * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int, InetAddress) instead} */ @Deprecated public java.net.ServerSocket createServerSocket(final int port, final int backlog, final InetAddress ifAddress) throws IOException { return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); } // Override the default implementation @Override public Socket createSocket() throws IOException { return this.context.getSocketFactory().createSocket(); } @Override public Socket createSocket(final InetAddress address, final int port) throws IOException { return this.context.getSocketFactory().createSocket(address, port); } // DEPRECATED METHODS - for API compatibility only - DO NOT USE @Override public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress, final int localPort) throws IOException { return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); } @Override public Socket createSocket(final String address, final int port) throws UnknownHostException, IOException { return this.context.getSocketFactory().createSocket(address, port); } @Override public Socket createSocket(final String address, final int port, final InetAddress localAddress, final int localPort) throws UnknownHostException, IOException { return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); } /** * @param socket the socket * @return the socket * @throws IOException on error * @deprecated (2.2) use {@link FTPSServerSocketFactory#init(java.net.ServerSocket)} */ @Deprecated public java.net.ServerSocket init(final java.net.ServerSocket socket) throws IOException { ((javax.net.ssl.SSLServerSocket) socket).setUseClientMode(true); return socket; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java000066400000000000000000000035321434047722200324610ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; import org.apache.commons.net.util.NetConstants; /** * Do not use. * * @since 2.0 * @deprecated 3.0 use {@link org.apache.commons.net.util.TrustManagerUtils#getValidateServerCertificateTrustManager() * TrustManagerUtils#getValidateServerCertificateTrustManager()} instead */ @Deprecated public class FTPSTrustManager implements X509TrustManager { /** * No-op */ @Override public void checkClientTrusted(final X509Certificate[] certificates, final String authType) { } @Override public void checkServerTrusted(final X509Certificate[] certificates, final String authType) throws CertificateException { for (final X509Certificate certificate : certificates) { certificate.checkValidity(); } } @Override public X509Certificate[] getAcceptedIssuers() { return NetConstants.EMPTY_X509_CERTIFICATE_ARRAY; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/package-info.java000066400000000000000000000015571434047722200317010ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * FTP and FTPS support classes */ package org.apache.commons.net.ftp;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/000077500000000000000000000000001434047722200277765ustar00rootroot00000000000000CompositeFileEntryParser.java000066400000000000000000000042731434047722200355310ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; import org.apache.commons.net.ftp.FTPFileEntryParserImpl; /** * This implementation allows to pack some FileEntryParsers together and handle the case where to returned dir style isn't clearly defined. The matching parser * will be cached. If the cached parser wont match due to the server changed the dir style, a new matching parser will be searched. */ public class CompositeFileEntryParser extends FTPFileEntryParserImpl { private final FTPFileEntryParser[] ftpFileEntryParsers; private FTPFileEntryParser cachedFtpFileEntryParser; public CompositeFileEntryParser(final FTPFileEntryParser[] ftpFileEntryParsers) { this.cachedFtpFileEntryParser = null; this.ftpFileEntryParsers = ftpFileEntryParsers; } @Override public FTPFile parseFTPEntry(final String listEntry) { if (cachedFtpFileEntryParser != null) { return cachedFtpFileEntryParser.parseFTPEntry(listEntry); } for (final FTPFileEntryParser ftpFileEntryParser : ftpFileEntryParsers) { final FTPFile matched = ftpFileEntryParser.parseFTPEntry(listEntry); if (matched != null) { cachedFtpFileEntryParser = ftpFileEntryParser; return matched; } } return null; } } ConfigurableFTPFileEntryParserImpl.java000066400000000000000000000113701434047722200373570ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import java.util.Calendar; import org.apache.commons.net.ftp.Configurable; import org.apache.commons.net.ftp.FTPClientConfig; /** *

* This abstract class implements the common timestamp parsing algorithm for all the concrete parsers. Classes derived from this one will parse file listings * via a supplied regular expression that pulls out the date portion as a separate string which is passed to the underlying {@link FTPTimestampParser delegate} * to handle parsing of the file timestamp. *

* This class also implements the {@link Configurable Configurable} interface to allow the parser to be configured from the outside. * * @since 1.4 */ public abstract class ConfigurableFTPFileEntryParserImpl extends RegexFTPFileEntryParserImpl implements Configurable { private final FTPTimestampParser timestampParser; /** * constructor for this abstract class. * * @param regex Regular expression used main parsing of the file listing. */ public ConfigurableFTPFileEntryParserImpl(final String regex) { super(regex); this.timestampParser = new FTPTimestampParserImpl(); } /** * constructor for this abstract class. * * @param regex Regular expression used main parsing of the file listing. * @param flags the flags to apply, see {@link java.util.regex.Pattern#compile(String, int) Pattern#compile(String, int)}. Use 0 for none. * @since 3.4 */ public ConfigurableFTPFileEntryParserImpl(final String regex, final int flags) { super(regex, flags); this.timestampParser = new FTPTimestampParserImpl(); } /** * Implementation of the {@link Configurable Configurable} interface. Configures this parser by delegating to the underlying Configurable FTPTimestampParser * implementation, ' passing it the supplied {@link FTPClientConfig FTPClientConfig} if that is non-null or a default configuration defined by each concrete * subclass. * * @param config the configuration to be used to configure this parser. If it is null, a default configuration defined by each concrete subclass is used * instead. */ @Override public void configure(final FTPClientConfig config) { if (this.timestampParser instanceof Configurable) { final FTPClientConfig defaultCfg = getDefaultConfiguration(); if (config != null) { if (null == config.getDefaultDateFormatStr()) { config.setDefaultDateFormatStr(defaultCfg.getDefaultDateFormatStr()); } if (null == config.getRecentDateFormatStr()) { config.setRecentDateFormatStr(defaultCfg.getRecentDateFormatStr()); } ((Configurable) this.timestampParser).configure(config); } else { ((Configurable) this.timestampParser).configure(defaultCfg); } } } /** * Each concrete subclass must define this member to create a default configuration to be used when that subclass is instantiated without a * {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for the subclass. */ protected abstract FTPClientConfig getDefaultConfiguration(); /** * This method is called by the concrete parsers to delegate timestamp parsing to the timestamp parser. * * @param timestampStr the timestamp string pulled from the file listing by the regular expression parser, to be submitted to the * timestampParser for extracting the timestamp. * @return a java.util.Calendar containing results of the timestamp parse. * @throws ParseException on parse error */ public Calendar parseTimestamp(final String timestampStr) throws ParseException { return this.timestampParser.parseTimestamp(timestampStr); } } DefaultFTPFileEntryParserFactory.java000066400000000000000000000265051434047722200370570ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.regex.Pattern; import org.apache.commons.net.ftp.Configurable; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFileEntryParser; /** * This is the default implementation of the FTPFileEntryParserFactory interface. This is the implementation that will be used by * org.apache.commons.net.ftp.FTPClient.listFiles() if no other implementation has been specified. * * @see org.apache.commons.net.ftp.FTPClient#listFiles * @see org.apache.commons.net.ftp.FTPClient#setParserFactory */ public class DefaultFTPFileEntryParserFactory implements FTPFileEntryParserFactory { // Match a plain Java Identifier private static final String JAVA_IDENTIFIER = "\\p{javaJavaIdentifierStart}(\\p{javaJavaIdentifierPart})*"; // Match a qualified name, e.g. a.b.c.Name - but don't allow the default package as that would allow "VMS"/"UNIX" etc. private static final String JAVA_QUALIFIED_NAME = "(" + JAVA_IDENTIFIER + "\\.)+" + JAVA_IDENTIFIER; // Create the pattern, as it will be reused many times private static final Pattern JAVA_QUALIFIED_NAME_PATTERN = Pattern.compile(JAVA_QUALIFIED_NAME); /** *

* Implementation extracts a key from the supplied {@link FTPClientConfig FTPClientConfig} parameter and creates an object implementing the interface * FTPFileEntryParser and uses the supplied configuration to configure it. *

*

* Note that this method will generally not be called in scenarios that call for autodetection of parser type but rather, for situations where the user * knows that the server uses a non-default configuration and knows what that configuration is. *

* * @param config A {@link FTPClientConfig FTPClientConfig} used to configure the parser created * * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. * @throws ParserInitializationException Thrown on any exception in instantiation * @throws NullPointerException if {@code config} is {@code null} * @since 1.4 */ @Override public FTPFileEntryParser createFileEntryParser(final FTPClientConfig config) throws ParserInitializationException { final String key = config.getServerSystemKey(); return createFileEntryParser(key, config); } /** * This default implementation of the FTPFileEntryParserFactory interface works according to the following logic: First it attempts to interpret the * supplied key as a fully qualified classname (default package is not allowed) of a class implementing the FTPFileEntryParser interface. If that succeeds, * a parser object of this class is instantiated and is returned; otherwise it attempts to interpret the key as an identirier commonly used by the FTP SYST * command to identify systems. *

* If key is not recognized as a fully qualified classname known to the system, this method will then attempt to see whether it contains * a string identifying one of the known parsers. This comparison is case-insensitive. The intent here is where possible, to select as keys strings * which are returned by the SYST command on the systems which the corresponding parser successfully parses. This enables this factory to be used in the * auto-detection system. * * @param key should be a fully qualified classname corresponding to a class implementing the FTPFileEntryParser interface
* OR
* a string containing (case-insensitively) one of the following keywords: *

    *
  • {@link FTPClientConfig#SYST_UNIX UNIX}
  • *
  • {@link FTPClientConfig#SYST_NT WINDOWS}
  • *
  • {@link FTPClientConfig#SYST_OS2 OS/2}
  • *
  • {@link FTPClientConfig#SYST_OS400 OS/400}
  • *
  • {@link FTPClientConfig#SYST_AS400 AS/400}
  • *
  • {@link FTPClientConfig#SYST_VMS VMS}
  • *
  • {@link FTPClientConfig#SYST_MVS MVS}
  • *
  • {@link FTPClientConfig#SYST_NETWARE NETWARE}
  • *
  • {@link FTPClientConfig#SYST_L8 TYPE:L8}
  • *
* @return the FTPFileEntryParser corresponding to the supplied key. * @throws ParserInitializationException thrown if for any reason the factory cannot resolve the supplied key into an FTPFileEntryParser. * @see FTPFileEntryParser */ @Override public FTPFileEntryParser createFileEntryParser(final String key) { if (key == null) { throw new ParserInitializationException("Parser key cannot be null"); } return createFileEntryParser(key, null); } // Common method to process both key and config parameters. private FTPFileEntryParser createFileEntryParser(final String key, final FTPClientConfig config) { FTPFileEntryParser parser = null; // Is the key a possible class name? if (JAVA_QUALIFIED_NAME_PATTERN.matcher(key).matches()) { try { final Class parserClass = Class.forName(key); try { parser = (FTPFileEntryParser) parserClass.newInstance(); } catch (final ClassCastException e) { throw new ParserInitializationException( parserClass.getName() + " does not implement the interface " + "org.apache.commons.net.ftp.FTPFileEntryParser.", e); } catch (final Exception | ExceptionInInitializerError e) { throw new ParserInitializationException("Error initializing parser", e); } } catch (final ClassNotFoundException e) { // OK, assume it is an alias } } if (parser == null) { // Now try for aliases final String ukey = key.toUpperCase(java.util.Locale.ENGLISH); if (ukey.contains(FTPClientConfig.SYST_UNIX_TRIM_LEADING)) { parser = new UnixFTPEntryParser(config, true); } // must check this after SYST_UNIX_TRIM_LEADING as it is a substring of it else if (ukey.contains(FTPClientConfig.SYST_UNIX)) { parser = new UnixFTPEntryParser(config, false); } else if (ukey.contains(FTPClientConfig.SYST_VMS)) { parser = new VMSVersioningFTPEntryParser(config); } else if (ukey.contains(FTPClientConfig.SYST_NT)) { parser = createNTFTPEntryParser(config); } else if (ukey.contains(FTPClientConfig.SYST_OS2)) { parser = new OS2FTPEntryParser(config); } else if (ukey.contains(FTPClientConfig.SYST_OS400) || ukey.contains(FTPClientConfig.SYST_AS400)) { parser = createOS400FTPEntryParser(config); } else if (ukey.contains(FTPClientConfig.SYST_MVS)) { parser = new MVSFTPEntryParser(); // Does not currently support config parameter } else if (ukey.contains(FTPClientConfig.SYST_NETWARE)) { parser = new NetwareFTPEntryParser(config); } else if (ukey.contains(FTPClientConfig.SYST_MACOS_PETER)) { parser = new MacOsPeterFTPEntryParser(config); } else if (ukey.contains(FTPClientConfig.SYST_L8)) { // L8 normally means Unix, but move it to the end for some L8 systems that aren't. // This check should be last! parser = new UnixFTPEntryParser(config); } else { throw new ParserInitializationException("Unknown parser type: " + key); } } if (parser instanceof Configurable) { ((Configurable) parser).configure(config); } return parser; } public FTPFileEntryParser createMVSEntryParser() { return new MVSFTPEntryParser(); } public FTPFileEntryParser createNetwareFTPEntryParser() { return new NetwareFTPEntryParser(); } public FTPFileEntryParser createNTFTPEntryParser() { return createNTFTPEntryParser(null); } /** * Creates an NT FTP parser: if the config exists, and the system key equals {@link FTPClientConfig#SYST_NT} then a plain {@link NTFTPEntryParser} is used, * otherwise a composite of {@link NTFTPEntryParser} and {@link UnixFTPEntryParser} is used. * * @param config the config to use, may be {@code null} * @return the parser */ private FTPFileEntryParser createNTFTPEntryParser(final FTPClientConfig config) { if (config != null && FTPClientConfig.SYST_NT.equals(config.getServerSystemKey())) { return new NTFTPEntryParser(config); } // clone the config as it may be changed by the parsers (NET-602) final FTPClientConfig config2 = config != null ? new FTPClientConfig(config) : null; return new CompositeFileEntryParser(new FTPFileEntryParser[] { new NTFTPEntryParser(config), new UnixFTPEntryParser(config2, config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) }); } public FTPFileEntryParser createOS2FTPEntryParser() { return new OS2FTPEntryParser(); } public FTPFileEntryParser createOS400FTPEntryParser() { return createOS400FTPEntryParser(null); } /** * Creates an OS400 FTP parser: if the config exists, and the system key equals {@link FTPClientConfig#SYST_OS400} then a plain {@link OS400FTPEntryParser} * is used, otherwise a composite of {@link OS400FTPEntryParser} and {@link UnixFTPEntryParser} is used. * * @param config the config to use, may be {@code null} * @return the parser */ private FTPFileEntryParser createOS400FTPEntryParser(final FTPClientConfig config) { if (config != null && FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey())) { return new OS400FTPEntryParser(config); } // clone the config as it may be changed by the parsers (NET-602) final FTPClientConfig config2 = config != null ? new FTPClientConfig(config) : null; return new CompositeFileEntryParser(new FTPFileEntryParser[] { new OS400FTPEntryParser(config), new UnixFTPEntryParser(config2, config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) }); } public FTPFileEntryParser createUnixFTPEntryParser() { return new UnixFTPEntryParser(); } public FTPFileEntryParser createVMSVersioningFTPEntryParser() { return new VMSVersioningFTPEntryParser(); } } EnterpriseUnixFTPEntryParser.java000066400000000000000000000135161434047722200363250ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.Calendar; import org.apache.commons.net.ftp.FTPFile; /** * Parser for the Connect Enterprise Unix FTP Server From Sterling Commerce. Here is a sample of the sort of output line this parser processes: * *
 * "-C--E-----FTP B QUA1I1      18128       41 Aug 12 13:56 QUADTEST"
 * 
*

* Note: EnterpriseUnixFTPEntryParser can only be instantiated through the DefaultFTPParserFactory by classname. It will not be chosen by the autodetection * scheme. *

* * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory */ public class EnterpriseUnixFTPEntryParser extends RegexFTPFileEntryParserImpl { /** * months abbreviations looked for by this parser. Also used to determine which month has been matched by the parser. */ private static final String MONTHS = "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"; /** * this is the regular expression used by this parser. */ private static final String REGEX = "(([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])" + "([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z]))" + "(\\S*)\\s*" // 12 + "(\\S+)\\s*" // 13 + "(\\S*)\\s*" // 14 user + "(\\d*)\\s*" // 15 group + "(\\d*)\\s*" // 16 filesize + MONTHS // 17 month + "\\s*" // TODO should the space be optional? // TODO \\d* should be \\d? surely ? Otherwise 01111 is allowed + "((?:[012]\\d*)|(?:3[01]))\\s*" // 18 date [012]\d* or 3[01] + "((\\d\\d\\d\\d)|((?:[01]\\d)|(?:2[0123])):([012345]\\d))\\s" // 20 \d\d\d\d = year OR // 21 [01]\d or 2[0123] hour + ':' // 22 [012345]\d = minute + "(\\S*)(\\s*.*)"; // 23 name /** * The sole constructor for a EnterpriseUnixFTPEntryParser object. * */ public EnterpriseUnixFTPEntryParser() { super(REGEX); } /** * Parses a line of a unix FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the file * listing line doesn't describe a file, null is returned, otherwise a FTPFile instance representing the files in the * directory is returned. * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { final FTPFile file = new FTPFile(); file.setRawListing(entry); if (matches(entry)) { final String usr = group(14); final String grp = group(15); final String filesize = group(16); final String mo = group(17); final String da = group(18); final String yr = group(20); final String hr = group(21); final String min = group(22); final String name = group(23); file.setType(FTPFile.FILE_TYPE); file.setUser(usr); file.setGroup(grp); try { file.setSize(Long.parseLong(filesize)); } catch (final NumberFormatException e) { // intentionally do nothing } final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MILLISECOND, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.HOUR_OF_DAY, 0); final int pos = MONTHS.indexOf(mo); final int month = pos / 4; final int missingUnit; // the first missing unit try { if (yr != null) { // it's a year; there are no hours and minutes cal.set(Calendar.YEAR, Integer.parseInt(yr)); missingUnit = Calendar.HOUR_OF_DAY; } else { // it must be hour/minute or we wouldn't have matched missingUnit = Calendar.SECOND; int year = cal.get(Calendar.YEAR); // if the month we're reading is greater than now, it must // be last year if (cal.get(Calendar.MONTH) < month) { year--; } cal.set(Calendar.YEAR, year); cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hr)); cal.set(Calendar.MINUTE, Integer.parseInt(min)); } cal.set(Calendar.MONTH, month); cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(da)); cal.clear(missingUnit); file.setTimestamp(cal); } catch (final NumberFormatException e) { // do nothing, date will be uninitialized } file.setName(name); return file; } return null; } } FTPFileEntryParserFactory.java000066400000000000000000000050011434047722200355360ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFileEntryParser; /** * The interface describes a factory for creating FTPFileEntryParsers. * * @since 1.2 */ public interface FTPFileEntryParserFactory { /** *

* Implementation should be a method that extracts a key from the supplied {@link FTPClientConfig FTPClientConfig} parameter and creates an object * implementing the interface FTPFileEntryParser and uses the supplied configuration to configure it. *

*

* Note that this method will generally not be called in scenarios that call for autodetection of parser type but rather, for situations where the user * knows that the server uses a non-default configuration and knows what that configuration is. *

* * @param config A {@link FTPClientConfig FTPClientConfig} used to configure the parser created * * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. * @throws ParserInitializationException Thrown on any exception in instantiation * @since 1.4 */ FTPFileEntryParser createFileEntryParser(FTPClientConfig config) throws ParserInitializationException; /** * Implementation should be a method that decodes the supplied key and creates an object implementing the interface FTPFileEntryParser. * * @param key A string that somehow identifies an FTPFileEntryParser to be created. * * @return the FTPFileEntryParser created. * @throws ParserInitializationException Thrown on any exception in instantiation */ FTPFileEntryParser createFileEntryParser(String key) throws ParserInitializationException; } FTPTimestampParser.java000066400000000000000000000035171434047722200342620ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import java.util.Calendar; /** * This interface specifies the concept of parsing an FTP server's timestamp. * * @since 1.4 */ public interface FTPTimestampParser { /** * the default default date format. */ String DEFAULT_SDF = UnixFTPEntryParser.DEFAULT_DATE_FORMAT; /** * the default recent date format. */ String DEFAULT_RECENT_SDF = UnixFTPEntryParser.DEFAULT_RECENT_DATE_FORMAT; /** * Parses the supplied datestamp parameter. This parameter typically would have been pulled from a longer FTP listing via the regular expression mechanism * * @param timestampStr - the timestamp portion of the FTP directory listing to be parsed * @return a java.util.Calendar object initialized to the date parsed by the parser * @throws ParseException if none of the parser mechanisms belonging to the implementor can parse the input. */ Calendar parseTimestamp(String timestampStr) throws ParseException; } FTPTimestampParserImpl.java000066400000000000000000000375471434047722200351160ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import org.apache.commons.net.ftp.Configurable; import org.apache.commons.net.ftp.FTPClientConfig; /** * Default implementation of the {@link FTPTimestampParser FTPTimestampParser} interface also implements the {@link org.apache.commons.net.ftp.Configurable * Configurable} interface to allow the parsing to be configured from the outside. * * @see ConfigurableFTPFileEntryParserImpl * @since 1.4 */ public class FTPTimestampParserImpl implements FTPTimestampParser, Configurable { /* * List of units in order of increasing significance. This allows the code to clear all units in the Calendar until it reaches the least significant unit in * the parse string. The date formats are analysed to find the least significant unit (e.g. Minutes or Milliseconds) and the appropriate index to the array * is saved. This is done by searching the array for the unit specifier, and returning the index. When clearing the Calendar units, the code loops through * the array until the previous entry. e.g. for MINUTE it would clear MILLISECOND and SECOND */ private static final int[] CALENDAR_UNITS = { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR }; /* * Return the index to the array representing the least significant unit found in the date format. Default is 0 (to avoid dropping precision) */ private static int getEntry(final SimpleDateFormat dateFormat) { if (dateFormat == null) { return 0; } final String FORMAT_CHARS = "SsmHdM"; final String pattern = dateFormat.toPattern(); for (final char ch : FORMAT_CHARS.toCharArray()) { if (pattern.indexOf(ch) != -1) { // found the character switch (ch) { case 'S': return indexOf(Calendar.MILLISECOND); case 's': return indexOf(Calendar.SECOND); case 'm': return indexOf(Calendar.MINUTE); case 'H': return indexOf(Calendar.HOUR_OF_DAY); case 'd': return indexOf(Calendar.DAY_OF_MONTH); case 'M': return indexOf(Calendar.MONTH); } } } return 0; } /* * Find the entry in the CALENDAR_UNITS array. */ private static int indexOf(final int calendarUnit) { int i; for (i = 0; i < CALENDAR_UNITS.length; i++) { if (calendarUnit == CALENDAR_UNITS[i]) { return i; } } return 0; } /* * Sets the Calendar precision (used by FTPFile#toFormattedDate) by clearing the immediately preceeding unit (if any). Unfortunately the clear(int) method * results in setting all other units. */ private static void setPrecision(final int index, final Calendar working) { if (index <= 0) { // e.g. MILLISECONDS return; } final int field = CALENDAR_UNITS[index - 1]; // Just in case the analysis is wrong, stop clearing if // field value is not the default. final int value = working.get(field); if (value != 0) { // don't reset if it has a value // new Throwable("Unexpected value "+value).printStackTrace(); // DEBUG } else { working.clear(field); // reset just the required field } } /** The date format for all dates, except possibly recent dates. Assumed to include the year. */ private SimpleDateFormat defaultDateFormat; /* The index in CALENDAR_UNITS of the smallest time unit in defaultDateFormat */ private int defaultDateSmallestUnitIndex; /** The format used for recent dates (which don't have the year). May be null. */ private SimpleDateFormat recentDateFormat; /* The index in CALENDAR_UNITS of the smallest time unit in recentDateFormat */ private int recentDateSmallestUnitIndex; private boolean lenientFutureDates; /** * The only constructor for this class. */ public FTPTimestampParserImpl() { setDefaultDateFormat(DEFAULT_SDF, null); setRecentDateFormat(DEFAULT_RECENT_SDF, null); } /** * Implementation of the {@link Configurable Configurable} interface. Configures this FTPTimestampParser according to the following logic: *

* Set up the {@link FTPClientConfig#setDefaultDateFormatStr(java.lang.String) defaultDateFormat} and optionally the * {@link FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat} to values supplied in the config based on month names configured as follows: *

*
    *
  • If a {@link FTPClientConfig#setShortMonthNames(String) shortMonthString} has been supplied in the config, use that to parse parse * timestamps.
  • *
  • Otherwise, if a {@link FTPClientConfig#setServerLanguageCode(String) serverLanguageCode} has been supplied in the config, use the month * names represented by that {@link FTPClientConfig#lookupDateFormatSymbols(String) language} to parse timestamps.
  • *
  • otherwise use default English month names
  • *
*

* Finally if a {@link org.apache.commons.net.ftp.FTPClientConfig#setServerTimeZoneId(String) serverTimeZoneId} has been supplied via the config, set that * into all date formats that have been configured. *

*/ @Override public void configure(final FTPClientConfig config) { DateFormatSymbols dfs = null; final String languageCode = config.getServerLanguageCode(); final String shortmonths = config.getShortMonthNames(); if (shortmonths != null) { dfs = FTPClientConfig.getDateFormatSymbols(shortmonths); } else if (languageCode != null) { dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode); } else { dfs = FTPClientConfig.lookupDateFormatSymbols("en"); } final String recentFormatString = config.getRecentDateFormatStr(); setRecentDateFormat(recentFormatString, dfs); final String defaultFormatString = config.getDefaultDateFormatStr(); if (defaultFormatString == null) { throw new IllegalArgumentException("defaultFormatString cannot be null"); } setDefaultDateFormat(defaultFormatString, dfs); setServerTimeZone(config.getServerTimeZoneId()); this.lenientFutureDates = config.isLenientFutureDates(); } /** * @return Returns the defaultDateFormat. */ public SimpleDateFormat getDefaultDateFormat() { return defaultDateFormat; } /** * @return Returns the defaultDateFormat pattern string. */ public String getDefaultDateFormatString() { return defaultDateFormat.toPattern(); } /** * @return Returns the recentDateFormat. */ public SimpleDateFormat getRecentDateFormat() { return recentDateFormat; } /** * @return Returns the recentDateFormat. */ public String getRecentDateFormatString() { return recentDateFormat.toPattern(); } /** * @return Returns the serverTimeZone used by this parser. */ public TimeZone getServerTimeZone() { return this.defaultDateFormat.getTimeZone(); } /** * @return returns an array of 12 strings representing the short month names used by this parse. */ public String[] getShortMonths() { return defaultDateFormat.getDateFormatSymbols().getShortMonths(); } /** * @return Returns the lenientFutureDates. */ boolean isLenientFutureDates() { return lenientFutureDates; } /** * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method} in the {@link FTPTimestampParser FTPTimestampParser} interface according to * this algorithm: * * If the recentDateFormat member has been defined, try to parse the supplied string with that. If that parse fails, or if the recentDateFormat member has * not been defined, attempt to parse with the defaultDateFormat member. If that fails, throw a ParseException. * * This method assumes that the server time is the same as the local time. * * @see FTPTimestampParserImpl#parseTimestamp(String, Calendar) * * @param timestampStr The timestamp to be parsed * @return a Calendar with the parsed timestamp */ @Override public Calendar parseTimestamp(final String timestampStr) throws ParseException { final Calendar now = Calendar.getInstance(); return parseTimestamp(timestampStr, now); } /** * If the recentDateFormat member has been defined, try to parse the supplied string with that. If that parse fails, or if the recentDateFormat member has * not been defined, attempt to parse with the defaultDateFormat member. If that fails, throw a ParseException. * * This method allows a {@link Calendar} instance to be passed in which represents the current (system) time. * * @see FTPTimestampParser#parseTimestamp(String) * @param timestampStr The timestamp to be parsed * @param serverTime The current time for the server * @return the calendar * @throws ParseException if timestamp cannot be parsed * @since 1.5 */ public Calendar parseTimestamp(final String timestampStr, final Calendar serverTime) throws ParseException { final Calendar working = (Calendar) serverTime.clone(); working.setTimeZone(getServerTimeZone()); // is this needed? Date parsed = null; if (recentDateFormat != null) { final Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it now.setTimeZone(this.getServerTimeZone()); if (lenientFutureDates) { // add a day to "now" so that "slop" doesn't cause a date // slightly in the future to roll back a full year. (Bug 35181 => NET-83) now.add(Calendar.DAY_OF_MONTH, 1); } // The Java SimpleDateFormat class uses the epoch year 1970 if not present in the input // As 1970 was not a leap year, it cannot parse "Feb 29" correctly. // Java 1.5+ returns Mar 1 1970 // Temporarily add the current year to the short date time // to cope with short-date leap year strings. // Since Feb 29 is more that 6 months from the end of the year, this should be OK for // all instances of short dates which are +- 6 months from current date. // TODO this won't always work for systems that use short dates +0/-12months // e.g. if today is Jan 1 2001 and the short date is Feb 29 final String year = Integer.toString(now.get(Calendar.YEAR)); final String timeStampStrPlusYear = timestampStr + " " + year; final SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy", recentDateFormat.getDateFormatSymbols()); hackFormatter.setLenient(false); hackFormatter.setTimeZone(recentDateFormat.getTimeZone()); final ParsePosition pp = new ParsePosition(0); parsed = hackFormatter.parse(timeStampStrPlusYear, pp); // Check if we parsed the full string, if so it must have been a short date originally if (parsed != null && pp.getIndex() == timeStampStrPlusYear.length()) { working.setTime(parsed); if (working.after(now)) { // must have been last year instead working.add(Calendar.YEAR, -1); } setPrecision(recentDateSmallestUnitIndex, working); return working; } } final ParsePosition pp = new ParsePosition(0); parsed = defaultDateFormat.parse(timestampStr, pp); // note, length checks are mandatory for us since // SimpleDateFormat methods will succeed if less than // full string is matched. They will also accept, // despite "leniency" setting, a two-digit number as // a valid year (e.g. 22:04 will parse as 22 A.D.) // so could mistakenly confuse an hour with a year, // if we don't insist on full length parsing. if ((parsed == null) || (pp.getIndex() != timestampStr.length())) { throw new ParseException("Timestamp '" + timestampStr + "' could not be parsed using a server time of " + serverTime.getTime().toString(), pp.getErrorIndex()); } working.setTime(parsed); setPrecision(defaultDateSmallestUnitIndex, working); return working; } /** * @param format The defaultDateFormat to be set. * @param dfs the symbols to use (may be null) */ private void setDefaultDateFormat(final String format, final DateFormatSymbols dfs) { if (format != null) { if (dfs != null) { this.defaultDateFormat = new SimpleDateFormat(format, dfs); } else { this.defaultDateFormat = new SimpleDateFormat(format); } this.defaultDateFormat.setLenient(false); } else { this.defaultDateFormat = null; } this.defaultDateSmallestUnitIndex = getEntry(this.defaultDateFormat); } /** * @param lenientFutureDates The lenientFutureDates to set. */ void setLenientFutureDates(final boolean lenientFutureDates) { this.lenientFutureDates = lenientFutureDates; } /** * @param format The recentDateFormat to set. * @param dfs the symbols to use (may be null) */ private void setRecentDateFormat(final String format, final DateFormatSymbols dfs) { if (format != null) { if (dfs != null) { this.recentDateFormat = new SimpleDateFormat(format, dfs); } else { this.recentDateFormat = new SimpleDateFormat(format); } this.recentDateFormat.setLenient(false); } else { this.recentDateFormat = null; } this.recentDateSmallestUnitIndex = getEntry(this.recentDateFormat); } /** * sets a TimeZone represented by the supplied ID string into all of the parsers used by this server. * * @param serverTimeZoneId Time Id java.util.TimeZone id used by the ftp server. If null the client's local time zone is assumed. */ private void setServerTimeZone(final String serverTimeZoneId) { TimeZone serverTimeZone = TimeZone.getDefault(); if (serverTimeZoneId != null) { serverTimeZone = TimeZone.getTimeZone(serverTimeZoneId); } this.defaultDateFormat.setTimeZone(serverTimeZone); if (this.recentDateFormat != null) { this.recentDateFormat.setTimeZone(serverTimeZone); } } } MLSxEntryParser.java000066400000000000000000000264741434047722200336210ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.time.Instant; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Locale; import java.util.TimeZone; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParserImpl; /** * Parser class for MSLT and MLSD replies. See RFC 3659. *

* Format is as follows: *

* *
 * entry            = [ facts ] SP pathname
 * facts            = 1*( fact ";" )
 * fact             = factname "=" value
 * factname         = "Size" / "Modify" / "Create" /
 *                    "Type" / "Unique" / "Perm" /
 *                    "Lang" / "Media-Type" / "CharSet" /
 * os-depend-fact / local-fact
 * os-depend-fact   = {IANA assigned OS name} "." token
 * local-fact       = "X." token
 * value            = *SCHAR
 *
 * Sample os-depend-fact:
 * UNIX.group=0;UNIX.mode=0755;UNIX.owner=0;
 * 
*

* A single control response entry (MLST) is returned with a leading space; multiple (data) entries are returned without any leading spaces. The parser requires * that the leading space from the MLST entry is removed. MLSD entries can begin with a single space if there are no facts. *

* * @since 3.0 */ public class MLSxEntryParser extends FTPFileEntryParserImpl { // This class is immutable, so a single instance can be shared. private static final MLSxEntryParser INSTANCE = new MLSxEntryParser(); private static final HashMap TYPE_TO_INT = new HashMap<>(); static { TYPE_TO_INT.put("file", Integer.valueOf(FTPFile.FILE_TYPE)); TYPE_TO_INT.put("cdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // listed directory TYPE_TO_INT.put("pdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // a parent dir TYPE_TO_INT.put("dir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // dir or sub-dir } private static final int[] UNIX_GROUPS = { // Groups in order of mode digits FTPFile.USER_ACCESS, FTPFile.GROUP_ACCESS, FTPFile.WORLD_ACCESS, }; private static final int[][] UNIX_PERMS = { // perm bits, broken down by octal int value /* 0 */ {}, /* 1 */ { FTPFile.EXECUTE_PERMISSION }, /* 2 */ { FTPFile.WRITE_PERMISSION }, /* 3 */ { FTPFile.EXECUTE_PERMISSION, FTPFile.WRITE_PERMISSION }, /* 4 */ { FTPFile.READ_PERMISSION }, /* 5 */ { FTPFile.READ_PERMISSION, FTPFile.EXECUTE_PERMISSION }, /* 6 */ { FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION }, /* 7 */ { FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION, FTPFile.EXECUTE_PERMISSION }, }; public static MLSxEntryParser getInstance() { return INSTANCE; } public static FTPFile parseEntry(final String entry) { return INSTANCE.parseFTPEntry(entry); } /** * Parse a GMT time stamp of the form yyyyMMDDHHMMSS[.sss] * * @param timestamp the date-time to parse * @return a Calendar entry, may be {@code null} * @since 3.4 */ public static Calendar parseGMTdateTime(final String timestamp) { final SimpleDateFormat dateFormat; final boolean hasMillis; if (timestamp.contains(".")) { dateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); hasMillis = true; } else { dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); hasMillis = false; } final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT"); // both time zones need to be set for the parse to work OK dateFormat.setTimeZone(gmtTimeZone); final GregorianCalendar gCalendar = new GregorianCalendar(gmtTimeZone); final ParsePosition pos = new ParsePosition(0); dateFormat.setLenient(false); // We want to parse the whole string final Date parsed = dateFormat.parse(timestamp, pos); if (pos.getIndex() != timestamp.length()) { return null; // did not fully parse the input } gCalendar.setTime(parsed); if (!hasMillis) { gCalendar.clear(Calendar.MILLISECOND); // flag up missing ms units } return gCalendar; } /** * Parse a GMT time stamp of the form yyyyMMDDHHMMSS[.sss] * * @param timestamp the date-time to parse * @return a Calendar entry, may be {@code null} * @since 3.9.0 */ public static Instant parseGmtInstant(final String timestamp) { return parseGMTdateTime(timestamp).toInstant(); } /** * Create the parser for MSLT and MSLD listing entries This class is immutable, so one can use {@link #getInstance()} instead. */ public MLSxEntryParser() { } // perm-fact = "Perm" "=" *pvals // pvals = "a" / "c" / "d" / "e" / "f" / // "l" / "m" / "p" / "r" / "w" private void doUnixPerms(final FTPFile file, final String valueLowerCase) { for (final char c : valueLowerCase.toCharArray()) { // TODO these are mostly just guesses at present switch (c) { case 'a': // (file) may APPEnd file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); break; case 'c': // (dir) files may be created in the dir file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); break; case 'd': // deletable file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); break; case 'e': // (dir) can change to this dir file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); break; case 'f': // (file) renamable // ?? file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); break; case 'l': // (dir) can be listed file.setPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION, true); break; case 'm': // (dir) can create directory here file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); break; case 'p': // (dir) entries may be deleted file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); break; case 'r': // (files) file may be RETRieved file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); break; case 'w': // (files) file may be STORed file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); break; default: break; // ignore unexpected flag for now. } // switch } // each char } @Override public FTPFile parseFTPEntry(final String entry) { if (entry.startsWith(" ")) {// leading space means no facts are present if (entry.length() > 1) { // is there a path name? final FTPFile file = new FTPFile(); file.setRawListing(entry); file.setName(entry.substring(1)); return file; } return null; // Invalid - no pathname } final String parts[] = entry.split(" ", 2); // Path may contain space if (parts.length != 2 || parts[1].isEmpty()) { return null; // no space found or no file name } final String factList = parts[0]; if (!factList.endsWith(";")) { return null; } final FTPFile file = new FTPFile(); file.setRawListing(entry); file.setName(parts[1]); final String[] facts = factList.split(";"); final boolean hasUnixMode = parts[0].toLowerCase(Locale.ENGLISH).contains("unix.mode="); for (final String fact : facts) { final String[] factparts = fact.split("=", -1); // Don't drop empty values // Sample missing permission // drwx------ 2 mirror mirror 4096 Mar 13 2010 subversion // modify=20100313224553;perm=;type=dir;unique=811U282598;UNIX.group=500;UNIX.mode=0700;UNIX.owner=500; subversion if (factparts.length != 2) { return null; // invalid - there was no "=" sign } final String factname = factparts[0].toLowerCase(Locale.ENGLISH); final String factvalue = factparts[1]; if (factvalue.isEmpty()) { continue; // nothing to see here } final String valueLowerCase = factvalue.toLowerCase(Locale.ENGLISH); if ("size".equals(factname) || "sizd".equals(factname)) { file.setSize(Long.parseLong(factvalue)); } else if ("modify".equals(factname)) { final Calendar parsed = parseGMTdateTime(factvalue); if (parsed == null) { return null; } file.setTimestamp(parsed); } else if ("type".equals(factname)) { final Integer intType = TYPE_TO_INT.get(valueLowerCase); if (intType == null) { file.setType(FTPFile.UNKNOWN_TYPE); } else { file.setType(intType.intValue()); } } else if (factname.startsWith("unix.")) { final String unixfact = factname.substring("unix.".length()).toLowerCase(Locale.ENGLISH); if ("group".equals(unixfact)) { file.setGroup(factvalue); } else if ("owner".equals(unixfact)) { file.setUser(factvalue); } else if ("mode".equals(unixfact)) { // e.g. 0[1]755 final int off = factvalue.length() - 3; // only parse last 3 digits for (int i = 0; i < 3; i++) { final int ch = factvalue.charAt(off + i) - '0'; if (ch >= 0 && ch <= 7) { // Check it's valid octal for (final int p : UNIX_PERMS[ch]) { file.setPermission(UNIX_GROUPS[i], p, true); } } else { // TODO should this cause failure, or can it be reported somehow? } } // digits } // mode } // unix. else if (!hasUnixMode && "perm".equals(factname)) { // skip if we have the UNIX.mode doUnixPerms(file, valueLowerCase); } // process "perm" } // each fact return file; } } MVSFTPEntryParser.java000066400000000000000000000450421434047722200340050ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import java.util.List; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** * Implementation of FTPFileEntryParser and FTPFileListParser for IBM zOS/MVS Systems. * * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) */ public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { static final int UNKNOWN_LIST_TYPE = -1; static final int FILE_LIST_TYPE = 0; static final int MEMBER_LIST_TYPE = 1; static final int UNIX_LIST_TYPE = 2; static final int JES_LEVEL_1_LIST_TYPE = 3; static final int JES_LEVEL_2_LIST_TYPE = 4; /** * Dates are ignored for file lists, but are used for member lists where possible */ static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd HH:mm"; // 2001/09/18 // 13:52 /** * Matches these entries: * *
     *  Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
     *  B10142 3390   2006/03/20  2   31  F       80    80  PS   MDI.OKL.WORK
     * 
* * @see Data set record formats */ static final String FILE_LIST_REGEX = "\\S+\\s+" + // volume // ignored "\\S+\\s+" + // unit - ignored "\\S+\\s+" + // access date - ignored "\\S+\\s+" + // extents -ignored // If the values are too large, the fields may be merged (NET-639) "(?:\\S+\\s+)?" + // used - ignored "(?:F|FB|V|VB|U)\\s+" + // recfm - F[B], V[B], U "\\S+\\s+" + // logical record length -ignored "\\S+\\s+" + // block size - ignored "(PS|PO|PO-E)\\s+" + // Dataset organisation. Many exist // but only support: PS, PO, PO-E "(\\S+)\\s*"; // Dataset Name (file name) /** * Matches these entries: * *
     *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
     *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
     * 
*/ static final String MEMBER_LIST_REGEX = "(\\S+)\\s+" + // name "\\S+\\s+" + // version, modification (ignored) "\\S+\\s+" + // create date (ignored) "(\\S+)\\s+" + // modification date "(\\S+)\\s+" + // modification time "\\S+\\s+" + // size in lines (ignored) "\\S+\\s+" + // size in lines at creation(ignored) "\\S+\\s+" + // lines modified (ignored) "\\S+\\s*"; // id of user who modified (ignored) /** * Matches these entries, note: no header: * *
     *   IBMUSER1  JOB01906  OUTPUT    3 Spool Files
     *   012345678901234567890123456789012345678901234
     *             1         2         3         4
     * 
*/ static final String JES_LEVEL_1_LIST_REGEX = "(\\S+)\\s+" + // job name ignored "(\\S+)\\s+" + // job number "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) "(\\S+)\\s+" + // number of spool files "(\\S+)\\s+" + // Text "Spool" ignored "(\\S+)\\s*" // Text "Files" ignored ; /** * JES INTERFACE LEVEL 2 parser Matches these entries: * *
     * JOBNAME  JOBID    OWNER    STATUS CLASS
     * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000 3 spool files
     * IBMUSER  TSU01830 IBMUSER  OUTPUT TSU      ABEND=522 3 spool files
     * 
* * Sample output from FTP session: * *
     * ftp> quote site filetype=jes
     * 200 SITE command was accepted
     * ftp> ls
     * 200 Port request OK.
     * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER
     * JOBNAME  JOBID    OWNER    STATUS CLASS
     * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000 3 spool files
     * IBMUSER  TSU01830 IBMUSER  OUTPUT TSU      ABEND=522 3 spool files
     * 250 List completed successfully.
     * ftp> ls job01906
     * 200 Port request OK.
     * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER
     * JOBNAME  JOBID    OWNER    STATUS CLASS
     * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000
     * --------
     * ID  STEPNAME PROCSTEP C DDNAME   BYTE-COUNT
     * 001 JES2              A JESMSGLG       858
     * 002 JES2              A JESJCL         128
     * 003 JES2              A JESYSMSG       443
     * 3 spool files
     * 250 List completed successfully.
     * 
*/ static final String JES_LEVEL_2_LIST_REGEX = "(\\S+)\\s+" + // job name ignored "(\\S+)\\s+" + // job number "(\\S+)\\s+" + // owner ignored "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) ignored "(\\S+)\\s+" + // job class ignored "(\\S+).*" // rest ignored ; private int isType = UNKNOWN_LIST_TYPE; /** * Fallback parser for Unix-style listings */ private UnixFTPEntryParser unixFTPEntryParser; /* * --------------------------------------------------------------------- Very brief and incomplete description of the zOS/MVS-file system. (Note: "zOS" is * the operating system on the mainframe, and is the new name for MVS) * * The file system on the mainframe does not have hierarchal structure as for example the unix file system. For a more comprehensive description, please * refer to the IBM manuals * * @LINK: http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/dgt2d440/CONTENTS * * * Dataset names ============= * * A dataset name consist of a number of qualifiers separated by '.', each qualifier can be at most 8 characters, and the total length of a dataset can be * max 44 characters including the dots. * * * Dataset organisation ==================== * * A dataset represents a piece of storage allocated on one or more disks. The structure of the storage is described with the field dataset organinsation * (DSORG). There are a number of dataset organisations, but only two are usable for FTP transfer. * * DSORG: PS: sequential, or flat file PO: partitioned dataset PO-E: extended partitioned dataset * * The PS file is just a flat file, as you would find it on the unix file system. * * The PO and PO-E files, can be compared to a single level directory structure. A PO file consist of a number of dataset members, or files if you will. It * is possible to CD into the file, and to retrieve the individual members. * * * Dataset record format ===================== * * The physical layout of the dataset is described on the dataset itself. There are a number of record formats (RECFM), but just a few is relavant for the * FTP transfer. * * Any one beginning with either F or V can safely used by FTP transfer. All others should only be used with great care. F means a fixed number of records * per allocated storage, and V means a variable number of records. * * * Other notes =========== * * The file system supports automatically backup and retrieval of datasets. If a file is backed up, the ftp LIST command will return: ARCIVE Not Direct * Access Device KJ.IOP998.ERROR.PL.UNITTEST * * * Implementation notes ==================== * * Only datasets that have dsorg PS, PO or PO-E and have recfm beginning with F or V or U, is fully parsed. * * The following fields in FTPFile is used: FTPFile.Rawlisting: Always set. FTPFile.Type: DIRECTORY_TYPE or FILE_TYPE or UNKNOWN FTPFile.Name: name * FTPFile.Timestamp: change time or null * * * * Additional information ====================== * * The MVS ftp server supports a number of features via the FTP interface. The features are controlled with the FTP command quote site * filetype= SEQ is the default and used for normal file transfer JES is used to interact with the Job Entry Subsystem (JES) similar to a job * scheduler DB2 is used to interact with a DB2 subsystem * * This parser supports SEQ and JES. * * * * * * */ /** * The sole constructor for a MVSFTPEntryParser object. * */ public MVSFTPEntryParser() { super(""); // note the regex is set in preParse. super.configure(null); // configure parser with default configurations } /* * @return */ @Override protected FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_MVS, DEFAULT_DATE_FORMAT, null); } /** * Parse entries representing a dataset list. Only datasets with DSORG PS or PO or PO-E and with RECFM F[B], V[B], U will be parsed. * * Format of ZOS/MVS file list: 1 2 3 4 5 6 7 8 9 10 Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname B10142 3390 2006/03/20 2 31 F 80 80 PS * MDI.OKL.WORK ARCIVE Not Direct Access Device KJ.IOP998.ERROR.PL.UNITTEST B1N231 3390 2006/03/20 1 15 VB 256 27998 PO PLU B1N231 3390 2006/03/20 1 15 VB * 256 27998 PO-E PLB * * ----------------------------------- Group within Regex [1] Volume [2] Unit [3] Referred [4] Ext: number of extents [5] Used [6] Recfm: Record format [7] * Lrecl: Logical record length [8] BlkSz: Block size [9] Dsorg: Dataset organisation. Many exists but only support: PS, PO, PO-E [10] Dsname: Dataset name * * Note: When volume is ARCIVE, it means the dataset is stored somewhere in a tape archive. These entries is currently not supported by this parser. A null * value is returned. * * @param entry zosDirectoryEntry * @return null: entry was not parsed. */ private FTPFile parseFileList(final String entry) { if (matches(entry)) { final FTPFile file = new FTPFile(); file.setRawListing(entry); final String name = group(2); final String dsorg = group(1); file.setName(name); // DSORG if ("PS".equals(dsorg)) { file.setType(FTPFile.FILE_TYPE); } else if ("PO".equals(dsorg) || "PO-E".equals(dsorg)) { // regex already ruled out anything other than PO or PO-E file.setType(FTPFile.DIRECTORY_TYPE); } else { return null; } return file; } return null; } /** * Parses a line of an z/OS - MVS FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the * file listing line doesn't describe a file, then null is returned. Otherwise a FTPFile instance representing the file is * returned. * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { switch (isType) { case FILE_LIST_TYPE: return parseFileList(entry); case MEMBER_LIST_TYPE: return parseMemberList(entry); case UNIX_LIST_TYPE: return unixFTPEntryParser.parseFTPEntry(entry); case JES_LEVEL_1_LIST_TYPE: return parseJeslevel1List(entry); case JES_LEVEL_2_LIST_TYPE: return parseJeslevel2List(entry); default: break; } return null; } /** * Matches these entries, note: no header: * *
     * [1]      [2]      [3]   [4] [5]
     * IBMUSER1 JOB01906 OUTPUT 3 Spool Files
     * 012345678901234567890123456789012345678901234
     *           1         2         3         4
     * -------------------------------------------
     * Group in regex
     * [1] Job name
     * [2] Job number
     * [3] Job status (INPUT,ACTIVE,OUTPUT)
     * [4] Number of sysout files
     * [5] The string "Spool Files"
     * 
* * @param entry zosDirectoryEntry * @return null: entry was not parsed. */ private FTPFile parseJeslevel1List(final String entry) { if (matches(entry)) { final FTPFile file = new FTPFile(); if (group(3).equalsIgnoreCase("OUTPUT")) { file.setRawListing(entry); final String name = group(2); /* Job Number, used by GET */ file.setName(name); file.setType(FTPFile.FILE_TYPE); return file; } } return null; } /** * Matches these entries: * *
     * [1]      [2]      [3]     [4]    [5]
     * JOBNAME  JOBID    OWNER   STATUS CLASS
     * IBMUSER1 JOB01906 IBMUSER OUTPUT A       RC=0000 3 spool files
     * IBMUSER  TSU01830 IBMUSER OUTPUT TSU     ABEND=522 3 spool files
     * 012345678901234567890123456789012345678901234
     *           1         2         3         4
     * -------------------------------------------
     * Group in regex
     * [1] Job name
     * [2] Job number
     * [3] Owner
     * [4] Job status (INPUT,ACTIVE,OUTPUT)
     * [5] Job Class
     * [6] The rest
     * 
* * @param entry zosDirectoryEntry * @return null: entry was not parsed. */ private FTPFile parseJeslevel2List(final String entry) { if (matches(entry)) { final FTPFile file = new FTPFile(); if (group(4).equalsIgnoreCase("OUTPUT")) { file.setRawListing(entry); final String name = group(2); /* Job Number, used by GET */ file.setName(name); file.setType(FTPFile.FILE_TYPE); return file; } } return null; } /** * Parse entries within a partitioned dataset. * * Format of a memberlist within a PDS: * *
     *    0         1        2          3        4     5     6      7    8
     *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
     *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
     *   TBTOOL    01.12 2002/09/12 2004/11/26 19:54    51    28     0 KIL001
     *
     * -------------------------------------------
     * [1] Name
     * [2] VV.MM: Version . modification
     * [3] Created: yyyy / MM / dd
     * [4,5] Changed: yyyy / MM / dd HH:mm
     * [6] Size: number of lines
     * [7] Init: number of lines when first created
     * [8] Mod: number of modified lines a last save
     * [9] Id: User id for last update
     * 
* * @param entry zosDirectoryEntry * @return null: entry was not parsed. */ private FTPFile parseMemberList(final String entry) { final FTPFile file = new FTPFile(); if (matches(entry)) { file.setRawListing(entry); final String name = group(1); final String datestr = group(2) + " " + group(3); file.setName(name); file.setType(FTPFile.FILE_TYPE); try { file.setTimestamp(super.parseTimestamp(datestr)); } catch (final ParseException e) { // just ignore parsing errors. // TODO check this is ok // Drop thru to try simple parser } return file; } /* * Assigns the name to the first word of the entry. Only to be used from a safe context, for example from a memberlist, where the regex for some reason * fails. Then just assign the name field of FTPFile. */ if (entry != null && !entry.trim().isEmpty()) { file.setRawListing(entry); final String name = entry.split(" ")[0]; file.setName(name); file.setType(FTPFile.FILE_TYPE); return file; } return null; } /** * preParse is called as part of the interface. Per definition is is called before the parsing takes place. Three kind of lists is recognize: z/OS-MVS File * lists z/OS-MVS Member lists unix file lists * * @since 2.0 */ @Override public List preParse(final List orig) { // simply remove the header line. Composite logic will take care of the // two different types of // list in short order. if (orig != null && !orig.isEmpty()) { final String header = orig.get(0); if (header.contains("Volume") && header.contains("Dsname")) { setType(FILE_LIST_TYPE); super.setRegex(FILE_LIST_REGEX); } else if (header.contains("Name") && header.contains("Id")) { setType(MEMBER_LIST_TYPE); super.setRegex(MEMBER_LIST_REGEX); } else if (header.indexOf("total") == 0) { setType(UNIX_LIST_TYPE); unixFTPEntryParser = new UnixFTPEntryParser(); } else if (header.indexOf("Spool Files") >= 30) { setType(JES_LEVEL_1_LIST_TYPE); super.setRegex(JES_LEVEL_1_LIST_REGEX); } else if (header.indexOf("JOBNAME") == 0 && header.indexOf("JOBID") > 8) {// header contains JOBNAME JOBID OWNER // STATUS CLASS setType(JES_LEVEL_2_LIST_TYPE); super.setRegex(JES_LEVEL_2_LIST_REGEX); } else { setType(UNKNOWN_LIST_TYPE); } if (isType != JES_LEVEL_1_LIST_TYPE) { // remove header is necessary orig.remove(0); } } return orig; } /** * Explicitly set the type of listing being processed. * * @param type The listing type. */ void setType(final int type) { isType = type; } } MacOsPeterFTPEntryParser.java000066400000000000000000000207461434047722200353460ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** * Implementation FTPFileEntryParser and FTPFileListParser for pre MacOS-X Systems. * * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) * @since 3.1 */ public class MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { static final String DEFAULT_DATE_FORMAT = "MMM d yyyy"; // Nov 9 2001 static final String DEFAULT_RECENT_DATE_FORMAT = "MMM d HH:mm"; // Nov 9 20:06 /** * this is the regular expression used by this parser. * * Permissions: r the file is readable w the file is writable x the file is executable - the indicated permission is not granted L mandatory locking occurs * during access (the set-group-ID bit is on and the group execution bit is off) s the set-user-ID or set-group-ID bit is on, and the corresponding user or * group execution bit is also on S undefined bit-state (the set-user-ID bit is on and the user execution bit is off) t the 1000 (octal) bit, or sticky bit, * is on [see chmod(1)], and execution is on T the 1000 bit is turned on, and execution is off (undefined bit- state) e z/OS external link bit */ private static final String REGEX = "([bcdelfmpSs-])" // type (1) + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+" // permission + "(" + "(folder\\s+)" + "|" + "((\\d+)\\s+(\\d+)\\s+)" // resource size & data size + ")" + "(\\d+)\\s+" // size /* * numeric or standard format date: yyyy-mm-dd (expecting hh:mm to follow) MMM [d]d [d]d MMM N.B. use non-space for MMM to allow for languages such * as German which use diacritics (e.g. umlaut) in some abbreviations. */ + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+" /* * year (for non-recent standard format) - yyyy or time (for numeric or recent standard format) [h]h:mm */ + "(\\d+(?::\\d+)?)\\s+" + "(\\S*)(\\s*.*)"; // the rest /** * The default constructor for a UnixFTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public MacOsPeterFTPEntryParser() { this(null); } /** * This constructor allows the creation of a UnixFTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public MacOsPeterFTPEntryParser(final FTPClientConfig config) { super(REGEX); configure(config); } /** * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for this parser. */ @Override protected FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT); } /** * Parses a line of a unix (standard) FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the * file listing line doesn't describe a file, null is returned, otherwise a FTPFile instance representing the files in the * directory is returned. * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { final FTPFile file = new FTPFile(); file.setRawListing(entry); final int type; boolean isDevice = false; if (matches(entry)) { final String typeStr = group(1); final String hardLinkCount = "0"; final String filesize = group(20); final String datestr = group(21) + " " + group(22); String name = group(23); final String endtoken = group(24); try { file.setTimestamp(super.parseTimestamp(datestr)); } catch (final ParseException e) { // intentionally do nothing } // A 'whiteout' file is an ARTIFICIAL entry in any of several types of // 'translucent' filesystems, of which a 'union' filesystem is one. // bcdelfmpSs- switch (typeStr.charAt(0)) { case 'd': type = FTPFile.DIRECTORY_TYPE; break; case 'e': // NET-39 => z/OS external link type = FTPFile.SYMBOLIC_LINK_TYPE; break; case 'l': type = FTPFile.SYMBOLIC_LINK_TYPE; break; case 'b': case 'c': isDevice = true; type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented break; case 'f': case '-': type = FTPFile.FILE_TYPE; break; default: // e.g. ? and w = whiteout type = FTPFile.UNKNOWN_TYPE; } file.setType(type); int g = 4; for (int access = 0; access < 3; access++, g += 4) { // Use != '-' to avoid having to check for suid and sticky bits file.setPermission(access, FTPFile.READ_PERMISSION, (!group(g).equals("-"))); file.setPermission(access, FTPFile.WRITE_PERMISSION, (!group(g + 1).equals("-"))); final String execPerm = group(g + 2); file.setPermission(access, FTPFile.EXECUTE_PERMISSION, !execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))); } if (!isDevice) { try { file.setHardLinkCount(Integer.parseInt(hardLinkCount)); } catch (final NumberFormatException e) { // intentionally do nothing } } file.setUser(null); file.setGroup(null); try { file.setSize(Long.parseLong(filesize)); } catch (final NumberFormatException e) { // intentionally do nothing } if (null == endtoken) { file.setName(name); } else { // oddball cases like symbolic links, file names // with spaces in them. name += endtoken; if (type == FTPFile.SYMBOLIC_LINK_TYPE) { final int end = name.indexOf(" -> "); // Give up if no link indicator is present if (end == -1) { file.setName(name); } else { file.setName(name.substring(0, end)); file.setLink(name.substring(end + 4)); } } else { file.setName(name); } } return file; } return null; } } NTFTPEntryParser.java000066400000000000000000000125631434047722200336630ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import java.util.regex.Pattern; import org.apache.commons.net.ftp.Configurable; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** * Implementation of FTPFileEntryParser and FTPFileListParser for NT Systems. * * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) */ public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { private static final String DEFAULT_DATE_FORMAT = "MM-dd-yy hh:mma"; // 11-09-01 12:30PM private static final String DEFAULT_DATE_FORMAT2 = "MM-dd-yy kk:mm"; // 11-09-01 18:30 /** * this is the regular expression used by this parser. */ private static final String REGEX = "(\\S+)\\s+(\\S+)\\s+" // MM-dd-yy whitespace hh:mma|kk:mm; swallow trailing spaces + "(?:()|([0-9]+))\\s+" // or ddddd; swallow trailing spaces + "(\\S.*)"; // First non-space followed by rest of line (name) private final FTPTimestampParser timestampParser; /** * The sole constructor for an NTFTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public NTFTPEntryParser() { this(null); } /** * This constructor allows the creation of an NTFTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public NTFTPEntryParser(final FTPClientConfig config) { super(REGEX, Pattern.DOTALL); configure(config); final FTPClientConfig config2 = new FTPClientConfig(FTPClientConfig.SYST_NT, DEFAULT_DATE_FORMAT2, null); config2.setDefaultDateFormatStr(DEFAULT_DATE_FORMAT2); this.timestampParser = new FTPTimestampParserImpl(); ((Configurable) this.timestampParser).configure(config2); } /** * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for this parser. */ @Override public FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_NT, DEFAULT_DATE_FORMAT, null); } /** * Parses a line of an NT FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the file * listing line doesn't describe a file, null is returned, otherwise a FTPFile instance representing the files in the * directory is returned. * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { final FTPFile f = new FTPFile(); f.setRawListing(entry); if (matches(entry)) { final String datestr = group(1) + " " + group(2); final String dirString = group(3); final String size = group(4); final String name = group(5); try { f.setTimestamp(super.parseTimestamp(datestr)); } catch (final ParseException e) { // parsing fails, try the other date format try { f.setTimestamp(timestampParser.parseTimestamp(datestr)); } catch (final ParseException e2) { // intentionally do nothing } } if (null == name || name.equals(".") || name.equals("..")) { return null; } f.setName(name); if ("".equals(dirString)) { f.setType(FTPFile.DIRECTORY_TYPE); f.setSize(0); } else { f.setType(FTPFile.FILE_TYPE); if (null != size) { f.setSize(Long.parseLong(size)); } } return f; } return null; } } NetwareFTPEntryParser.java000066400000000000000000000152071434047722200347450ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** * Implementation of FTPFileEntryParser and FTPFileListParser for Netware Systems. Note that some of the proprietary extensions for Novell-specific operations * are not supported. See * http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html for more details. * * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) * @since 1.5 */ public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { /** * Default date format is e.g. Feb 22 2006 */ private static final String DEFAULT_DATE_FORMAT = "MMM dd yyyy"; /** * Default recent date format is e.g. Feb 22 17:32 */ private static final String DEFAULT_RECENT_DATE_FORMAT = "MMM dd HH:mm"; /** * this is the regular expression used by this parser. Example: d [-W---F--] SCION_VOL2 512 Apr 13 23:12 VOL2 */ private static final String REGEX = "(d|-){1}\\s+" // Directory/file flag + "\\[([-A-Z]+)\\]\\s+" // Attributes RWCEAFMS or - + "(\\S+)\\s+" + "(\\d+)\\s+" // Owner and size + "(\\S+\\s+\\S+\\s+((\\d+:\\d+)|(\\d{4})))" // Long/short date format + "\\s+(.*)"; // Filename (incl. spaces) /** * The default constructor for a NetwareFTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public NetwareFTPEntryParser() { this(null); } /** * This constructor allows the creation of an NetwareFTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public NetwareFTPEntryParser(final FTPClientConfig config) { super(REGEX); configure(config); } /** * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for this parser. */ @Override protected FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_NETWARE, DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT); } /** * Parses a line of an NetwareFTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the file * listing line doesn't describe a file, null is returned, otherwise a FTPFile instance representing the files in the * directory is returned. *

* Netware file permissions are in the following format: RWCEAFMS, and are explained as follows: *

    *
  • S - Supervisor; All rights. *
  • R - Read; Right to open and read or execute. *
  • W - Write; Right to open and modify. *
  • C - Create; Right to create; when assigned to a file, allows a deleted file to be recovered. *
  • E - Erase; Right to delete. *
  • M - Modify; Right to rename a file and to change attributes. *
  • F - File Scan; Right to see directory or file listings. *
  • A - Access Control; Right to modify trustee assignments and the Inherited Rights Mask. *
* * See here for more details * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { final FTPFile f = new FTPFile(); if (matches(entry)) { final String dirString = group(1); final String attrib = group(2); final String user = group(3); final String size = group(4); final String datestr = group(5); final String name = group(9); try { f.setTimestamp(super.parseTimestamp(datestr)); } catch (final ParseException e) { // intentionally do nothing } // is it a DIR or a file if (dirString.trim().equals("d")) { f.setType(FTPFile.DIRECTORY_TYPE); } else // Should be "-" { f.setType(FTPFile.FILE_TYPE); } f.setUser(user); // set the name f.setName(name.trim()); // set the size f.setSize(Long.parseLong(size.trim())); // Now set the permissions (or at least a subset thereof - full permissions would probably require // subclassing FTPFile and adding extra metainformation there) if (attrib.indexOf('R') != -1) { f.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); } if (attrib.indexOf('W') != -1) { f.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); } return f; } return null; } } OS2FTPEntryParser.java000066400000000000000000000107121434047722200337370ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** * Implementation of FTPFileEntryParser and FTPFileListParser for OS2 Systems. * * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) */ public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl { private static final String DEFAULT_DATE_FORMAT = "MM-dd-yy HH:mm"; // 11-09-01 12:30 /** * this is the regular expression used by this parser. */ private static final String REGEX = "\\s*([0-9]+)\\s*" + "(\\s+|[A-Z]+)\\s*" + "(DIR|\\s+)\\s*" + "(\\S+)\\s+(\\S+)\\s+" /* date stuff */ + "(\\S.*)"; /** * The default constructor for a OS2FTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public OS2FTPEntryParser() { this(null); } /** * This constructor allows the creation of an OS2FTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public OS2FTPEntryParser(final FTPClientConfig config) { super(REGEX); configure(config); } /** * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for this parser. */ @Override protected FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_OS2, DEFAULT_DATE_FORMAT, null); } /** * Parses a line of an OS2 FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the file * listing line doesn't describe a file, null is returned, otherwise a FTPFile instance representing the files in the * directory is returned. * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { final FTPFile f = new FTPFile(); if (matches(entry)) { final String size = group(1); final String attrib = group(2); final String dirString = group(3); final String datestr = group(4) + " " + group(5); final String name = group(6); try { f.setTimestamp(super.parseTimestamp(datestr)); } catch (final ParseException e) { // intentionally do nothing } // is it a DIR or a file if (dirString.trim().equals("DIR") || attrib.trim().equals("DIR")) { f.setType(FTPFile.DIRECTORY_TYPE); } else { f.setType(FTPFile.FILE_TYPE); } // set the name f.setName(name.trim()); // set the size f.setSize(Long.parseLong(size.trim())); return f; } return null; } } OS400FTPEntryParser.java000066400000000000000000000342361434047722200341100ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.io.File; import java.text.ParseException; import java.util.Locale; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** *
 * Example *FILE/*MEM FTP entries, when the current
 * working directory is a file of file system QSYS:
 * ------------------------------------------------
 *
 * $ cwd /qsys.lib/rpgunit.lib/rpgunitc1.file
 *   250-NAMEFMT set to 1.
 *   250 "/QSYS.LIB/RPGUNIT.LIB/RPGUNITC1.FILE" is current directory.
 * $ dir
 *   227 Entering Passive Mode (10,200,36,33,40,249).
 *   125 List started.
 *   QPGMR          135168 22.06.13 13:18:19 *FILE
 *   QPGMR                                   *MEM       MKCMD.MBR
 *   QPGMR                                   *MEM       RUCALLTST.MBR
 *   QPGMR                                   *MEM       RUCMDHLP.MBR
 *   QPGMR                                   *MEM       RUCRTTST.MBR
 *   250 List completed.
 *
 *
 * Example *FILE entry of an OS/400 save file:
 * ---------------------------------------------------
 *
 * $ cwd /qsys.lib/rpgunit.lib
 *   250 "/QSYS.LIB/RPGUNIT.LIB" is current library.
 * $ dir rpgunit.file
 *   227 Entering Passive Mode (10,200,36,33,188,106).
 *   125 List started.
 *   QPGMR        16347136 29.06.13 15:45:09 *FILE      RPGUNIT.SAVF
 *   250 List completed.
 *
 *
 * Example *STMF/*DIR FTP entries, when the
 * current working directory is in file system "root":
 * ---------------------------------------------------
 *
 * $ cwd /home/raddatz
 *   250 "/home/raddatz" is current directory.
 * $ dir test*
 *   227 Entering Passive Mode (10,200,36,33,200,189).
 *   125 List started.
 *   RADDATZ           200 21.05.11 12:31:18 *STMF      TEST_RG_02_CRLF.properties
 *   RADDATZ           187 08.05.11 12:31:40 *STMF      TEST_RG_02_LF.properties
 *   RADDATZ           187 08.05.11 12:31:52 *STMF      TEST_RG_02_CR.properties
 *   RADDATZ          8192 04.07.13 09:04:14 *DIR       testDir1/
 *   RADDATZ          8192 04.07.13 09:04:17 *DIR       testDir2/
 *   250 List completed.
 *
 *
 * Example 1, using ANT to list specific members of a file:
 * --------------------------------------------------------
 *
 *      <echo/>
 *      <echo>Listing members of a file:</echo>
 *
 *      <ftp action="list"
 *           server="${ftp.server}"
 *           userid="${ftp.user}"
 *           password="${ftp.password}"
 *           binary="false"
 *           verbose="true"
 *           remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
 *           systemTypeKey="OS/400"
 *           listing="ftp-listing.txt"
 *           >
 *          <fileset dir="./i5-downloads-file" casesensitive="false">
 *              <include name="run*.mbr" />
 *          </fileset>
 *      </ftp>
 *
 * Output:
 * -------
 *
 *   [echo] Listing members of a file:
 *    [ftp] listing files
 *    [ftp] listing RUN.MBR
 *    [ftp] listing RUNNER.MBR
 *    [ftp] listing RUNNERBND.MBR
 *    [ftp] 3 files listed
 *
 *
 * Example 2, using ANT to list specific members of all files of a library:
 * ------------------------------------------------------------------------
 *
 *      <echo/>
 *      <echo>Listing members of all files of a library:</echo>
 *
 *      <ftp action="list"
 *           server="${ftp.server}"
 *           userid="${ftp.user}"
 *           password="${ftp.password}"
 *           binary="false"
 *           verbose="true"
 *           remotedir="/QSYS.LIB/RPGUNIT.LIB"
 *           systemTypeKey="OS/400"
 *           listing="ftp-listing.txt"
 *           >
 *          <fileset dir="./i5-downloads-lib" casesensitive="false">
 *              <include name="**\run*.mbr" />
 *          </fileset>
 *      </ftp>
 *
 * Output:
 * -------
 *
 *   [echo] Listing members of all files of a library:
 *    [ftp] listing files
 *    [ftp] listing RPGUNIT1.FILE\RUN.MBR
 *    [ftp] listing RPGUNIT1.FILE\RUNRMT.MBR
 *    [ftp] listing RPGUNITT1.FILE\RUNT.MBR
 *    [ftp] listing RPGUNITY1.FILE\RUN.MBR
 *    [ftp] listing RPGUNITY1.FILE\RUNNER.MBR
 *    [ftp] listing RPGUNITY1.FILE\RUNNERBND.MBR
 *    [ftp] 6 files listed
 *
 *
 * Example 3, using ANT to download specific members of a file:
 * ------------------------------------------------------------
 *
 *      <echo/>
 *      <echo>Downloading members of a file:</echo>
 *
 *      <ftp action="get"
 *           server="${ftp.server}"
 *           userid="${ftp.user}"
 *           password="${ftp.password}"
 *           binary="false"
 *           verbose="true"
 *           remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
 *           systemTypeKey="OS/400"
 *           >
 *          <fileset dir="./i5-downloads-file" casesensitive="false">
 *              <include name="run*.mbr" />
 *          </fileset>
 *      </ftp>
 *
 * Output:
 * -------
 *
 *   [echo] Downloading members of a file:
 *    [ftp] getting files
 *    [ftp] transferring RUN.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUN.MBR
 *    [ftp] transferring RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNER.MBR
 *    [ftp] transferring RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT - FTP\i5-downloads-file\RUNNERBND.MBR
 *    [ftp] 3 files retrieved
 *
 *
 * Example 4, using ANT to download specific members of all files of a library:
 * ----------------------------------------------------------------------------
 *
 *      <echo/>
 *      <echo>Downloading members of all files of a library:</echo>
 *
 *      <ftp action="get"
 *           server="${ftp.server}"
 *           userid="${ftp.user}"
 *           password="${ftp.password}"
 *           binary="false"
 *           verbose="true"
 *           remotedir="/QSYS.LIB/RPGUNIT.LIB"
 *           systemTypeKey="OS/400"
 *           >
 *          <fileset dir="./i5-downloads-lib" casesensitive="false">
 *              <include name="**\run*.mbr" />
 *          </fileset>
 *      </ftp>
 *
 * Output:
 * -------
 *
 *   [echo] Downloading members of all files of a library:
 *    [ftp] getting files
 *    [ftp] transferring RPGUNIT1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUN.MBR
 *    [ftp] transferring RPGUNIT1.FILE\RUNRMT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNIT1.FILE\RUNRMT.MBR
 *    [ftp] transferring RPGUNITT1.FILE\RUNT.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITT1.FILE\RUNT.MBR
 *    [ftp] transferring RPGUNITY1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUN.MBR
 *    [ftp] transferring RPGUNITY1.FILE\RUNNER.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNER.MBR
 *    [ftp] transferring RPGUNITY1.FILE\RUNNERBND.MBR to C:\work\rdp_080\space\ANT - FTP\i5-downloads\RPGUNITY1.FILE\RUNNERBND.MBR
 *    [ftp] 6 files retrieved
 *
 *
 * Example 5, using ANT to download a save file of a library:
 * ----------------------------------------------------------
 *
 *      <ftp action="get"
 *           server="${ftp.server}"
 *           userid="${ftp.user}"
 *           password="${ftp.password}"
 *           binary="true"
 *           verbose="true"
 *           remotedir="/QSYS.LIB/RPGUNIT.LIB"
 *           systemTypeKey="OS/400"
 *           >
 *        <fileset dir="./i5-downloads-savf" casesensitive="false">
 *            <include name="RPGUNIT.SAVF" />
 *        </fileset>
 *      </ftp>
 *
 * Output:
 * -------
 *   [echo] Downloading save file:
 *    [ftp] getting files
 *    [ftp] transferring RPGUNIT.SAVF to C:\workspaces\rdp_080\workspace\net-Test\i5-downloads-lib\RPGUNIT.SAVF
 *    [ftp] 1 files retrieved
 *
 * 
*/ public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl { private static final String DEFAULT_DATE_FORMAT = "yy/MM/dd HH:mm:ss"; // 01/11/09 12:30:24 private static final String REGEX = "(\\S+)\\s+" // user + "(?:(\\d+)\\s+)?" // size, empty for members + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+" // *STMF/*DIR/*FILE/*MEM + "((\\S+\\s*)+)?"; // file name, missing, when CWD is a *FILE /** * The default constructor for a OS400FTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public OS400FTPEntryParser() { this(null); } /** * This constructor allows the creation of an OS400FTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public OS400FTPEntryParser(final FTPClientConfig config) { super(REGEX); configure(config); } /** * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for this parser. */ @Override protected FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_OS400, DEFAULT_DATE_FORMAT, null); } /** * * @param string String value that is checked for null or empty. * @return true for null or empty values, else false. */ private boolean isNullOrEmpty(final String string) { return string == null || string.isEmpty(); } @Override public FTPFile parseFTPEntry(final String entry) { final FTPFile file = new FTPFile(); file.setRawListing(entry); final int type; if (matches(entry)) { final String usr = group(1); final String filesize = group(2); String datestr = ""; if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4))) { datestr = group(3) + " " + group(4); } final String typeStr = group(5); String name = group(6); boolean mustScanForPathSeparator = true; try { file.setTimestamp(super.parseTimestamp(datestr)); } catch (final ParseException e) { // intentionally do nothing } if (typeStr.equalsIgnoreCase("*STMF")) { type = FTPFile.FILE_TYPE; if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { return null; } } else if (typeStr.equalsIgnoreCase("*DIR")) { type = FTPFile.DIRECTORY_TYPE; if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { return null; } } else if (typeStr.equalsIgnoreCase("*FILE")) { // File, defines the structure of the data (columns of a row) // but the data is stored in one or more members. Typically a // source file contains multiple members whereas it is // recommended (but not enforced) to use one member per data // file. // Save files are a special type of files which are used // to save objects, e.g. for backups. if ((name == null) || !name.toUpperCase(Locale.ROOT).endsWith(".SAVF")) { return null; } mustScanForPathSeparator = false; type = FTPFile.FILE_TYPE; } else if (typeStr.equalsIgnoreCase("*MEM")) { mustScanForPathSeparator = false; type = FTPFile.FILE_TYPE; if (isNullOrEmpty(name)) { return null; } if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr))) { return null; } // Quick and dirty bug fix to make SelectorUtils work. // Class SelectorUtils uses 'File.separator' to splitt // a given path into pieces. But actually it had to // use the separator of the FTP server, which is a forward // slash in case of an AS/400. name = name.replace('/', File.separatorChar); } else { type = FTPFile.UNKNOWN_TYPE; } file.setType(type); file.setUser(usr); try { file.setSize(Long.parseLong(filesize)); } catch (final NumberFormatException e) { // intentionally do nothing } if (name.endsWith("/")) { name = name.substring(0, name.length() - 1); } if (mustScanForPathSeparator) { final int pos = name.lastIndexOf('/'); if (pos > -1) { name = name.substring(pos + 1); } } file.setName(name); return file; } return null; } } ParserInitializationException.java000066400000000000000000000040101434047722200366000ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; /** * This class encapsulates all errors that may be thrown by the process of an FTPFileEntryParserFactory creating and instantiating an FTPFileEntryParser. */ public class ParserInitializationException extends RuntimeException { private static final long serialVersionUID = 5563335279583210658L; /** * Constucts a ParserInitializationException with just a message * * @param message Exception message */ public ParserInitializationException(final String message) { super(message); } /** * Constucts a ParserInitializationException with a message and a root cause. * * @param message Exception message * @param rootCause root cause throwable that caused this to be thrown */ public ParserInitializationException(final String message, final Throwable rootCause) { super(message, rootCause); } /** * returns the root cause of this exception or null if no root cause was specified. * * @return the root cause of this exception being thrown * @deprecated use {@link #getCause()} instead */ @Deprecated public Throwable getRootCause() { return super.getCause(); } } RegexFTPFileEntryParserImpl.java000066400000000000000000000152741434047722200360400ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.apache.commons.net.ftp.FTPFileEntryParserImpl; /** * This abstract class implements both the older FTPFileListParser and newer FTPFileEntryParser interfaces with default functionality. All the classes in the * parser subpackage inherit from this. * * This is the base class for all regular expression based FTPFileEntryParser classes */ public abstract class RegexFTPFileEntryParserImpl extends FTPFileEntryParserImpl { /** * internal pattern the matcher tries to match, representing a file entry */ private Pattern pattern; /** * internal match result used by the parser */ private MatchResult result; /** * Internal PatternMatcher object used by the parser. It has protected scope in case subclasses want to make use of it for their own purposes. */ protected Matcher _matcher_; /** * The constructor for a RegexFTPFileEntryParserImpl object. The expression is compiled with flags = 0. * * @param regex The regular expression with which this object is initialized. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen in normal conditions. It it is seen, this is a sign * that a subclass has been created with a bad regular expression. Since the parser must be created before use, this means * that any bad parser subclasses created from this will bomb very quickly, leading to easy detection. */ public RegexFTPFileEntryParserImpl(final String regex) { compileRegex(regex, 0); } /** * The constructor for a RegexFTPFileEntryParserImpl object. * * @param regex The regular expression with which this object is initialized. * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen in normal conditions. It it is seen, this is a sign * that a subclass has been created with a bad regular expression. Since the parser must be created before use, this means * that any bad parser subclasses created from this will bomb very quickly, leading to easy detection. * @since 3.4 */ public RegexFTPFileEntryParserImpl(final String regex, final int flags) { compileRegex(regex, flags); } /** * Compile the regex and store the {@link Pattern}. * * This is an internal method to do the work so the constructor does not have to call an overrideable method. * * @param regex the expression to compile * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. * @throws IllegalArgumentException if the regex cannot be compiled */ private void compileRegex(final String regex, final int flags) { try { pattern = Pattern.compile(regex, flags); } catch (final PatternSyntaxException pse) { throw new IllegalArgumentException("Unparseable regex supplied: " + regex); } } /** * Convenience method * * @return the number of groups() in the internal MatchResult. */ public int getGroupCnt() { if (this.result == null) { return 0; } return this.result.groupCount(); } /** * For debugging purposes - returns a string shows each match group by number. * * @return a string shows each match group by number. */ public String getGroupsAsString() { final StringBuilder b = new StringBuilder(); for (int i = 1; i <= this.result.groupCount(); i++) { b.append(i).append(") ").append(this.result.group(i)).append(System.lineSeparator()); } return b.toString(); } /** * Convenience method delegates to the internal MatchResult's group() method. * * @param matchnum match group number to be retrieved * * @return the content of the matchnum'th group of the internal match or null if this method is called without a match having been made. */ public String group(final int matchnum) { if (this.result == null) { return null; } return this.result.group(matchnum); } /** * Convenience method delegates to the internal MatchResult's matches() method. * * @param s the String to be matched * @return true if s matches this object's regular expression. */ public boolean matches(final String s) { this.result = null; _matcher_ = pattern.matcher(s); if (_matcher_.matches()) { this.result = _matcher_.toMatchResult(); } return null != this.result; } /** * Alter the current regular expression being utilised for entry parsing and create a new {@link Pattern} instance. * * @param regex The new regular expression * @return true * @since 2.0 * @throws IllegalArgumentException if the regex cannot be compiled */ public boolean setRegex(final String regex) { compileRegex(regex, 0); return true; } /** * Alter the current regular expression being utilised for entry parsing and create a new {@link Pattern} instance. * * @param regex The new regular expression * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. * @return true * @since 3.4 * @throws IllegalArgumentException if the regex cannot be compiled */ public boolean setRegex(final String regex, final int flags) { compileRegex(regex, flags); return true; } } UnixFTPEntryParser.java000066400000000000000000000305371434047722200342660ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.ParseException; import java.util.List; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** * Implementation FTPFileEntryParser and FTPFileListParser for standard Unix Systems. * * This class is based on the logic of Daniel Savarese's DefaultFTPListParser, but adapted to use regular expressions and to fit the new FTPFileEntryParser * interface. * * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) */ public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { static final String DEFAULT_DATE_FORMAT = "MMM d yyyy"; // Nov 9 2001 static final String DEFAULT_RECENT_DATE_FORMAT = "MMM d HH:mm"; // Nov 9 20:06 static final String NUMERIC_DATE_FORMAT = "yyyy-MM-dd HH:mm"; // 2001-11-09 20:06 // Suffixes used in Japanese listings after the numeric values private static final String JA_MONTH = "\u6708"; private static final String JA_DAY = "\u65e5"; private static final String JA_YEAR = "\u5e74"; private static final String DEFAULT_DATE_FORMAT_JA = "M'" + JA_MONTH + "' d'" + JA_DAY + "' yyyy'" + JA_YEAR + "'"; // 6月 3日 2003年 private static final String DEFAULT_RECENT_DATE_FORMAT_JA = "M'" + JA_MONTH + "' d'" + JA_DAY + "' HH:mm"; // 8月 17日 20:10 /** * Some Linux distributions are now shipping an FTP server which formats file listing dates in an all-numeric format: "yyyy-MM-dd HH:mm. This * is a very welcome development, and hopefully it will soon become the standard. However, since it is so new, for now, and possibly forever, we merely * accomodate it, but do not make it the default. *

* For now end users may specify this format only via UnixFTPEntryParser(FTPClientConfig). Steve Cohen - 2005-04-17 */ public static final FTPClientConfig NUMERIC_DATE_CONFIG = new FTPClientConfig(FTPClientConfig.SYST_UNIX, NUMERIC_DATE_FORMAT, null); /** * this is the regular expression used by this parser. * * Permissions: r the file is readable w the file is writable x the file is executable - the indicated permission is not granted L mandatory locking occurs * during access (the set-group-ID bit is on and the group execution bit is off) s the set-user-ID or set-group-ID bit is on, and the corresponding user or * group execution bit is also on S undefined bit-state (the set-user-ID bit is on and the user execution bit is off) t the 1000 (octal) bit, or sticky bit, * is on [see chmod(1)], and execution is on T the 1000 bit is turned on, and execution is off (undefined bit- state) e z/OS external link bit Final letter * may be appended: + file has extended security attributes (e.g. ACL) Note: local listings on MacOSX also use '@'; this is not allowed for here as does not * appear to be shown by FTP servers {@code @} file has extended attributes */ private static final String REGEX = "([bcdelfmpSs-])" // file type + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?" // permissions + "\\s*" // separator TODO why allow it to be omitted?? + "(\\d+)" // link count + "\\s+" // separator + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces) + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces) + "(\\d+(?:,\\s*\\d+)?)" // size or n,m + "\\s+" // separator /* * numeric or standard format date: yyyy-mm-dd (expecting hh:mm to follow) MMM [d]d [d]d MMM N.B. use non-space for MMM to allow for languages such * as German which use diacritics (e.g. umlaut) in some abbreviations. Japanese uses numeric day and month with suffixes to distinguish them [d]dXX * [d]dZZ */ + "(" + "(?:\\d+[-/]\\d+[-/]\\d+)" + // yyyy-mm-dd "|(?:\\S{3}\\s+\\d{1,2})" + // MMM [d]d "|(?:\\d{1,2}\\s+\\S{3})" + // [d]d MMM "|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}" + JA_DAY + ")" + ")" + "\\s+" // separator /* * year (for non-recent standard format) - yyyy or time (for numeric or recent standard format) [h]h:mm or Japanese year - yyyyXX */ + "((?:\\d+(?::\\d+)?)|(?:\\d{4}" + JA_YEAR + "))" // (20) + "\\s" // separator + "(.*)"; // the rest (21) // if true, leading spaces are trimmed from file names // this was the case for the original implementation final boolean trimLeadingSpaces; // package protected for access from test code /** * The default constructor for a UnixFTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public UnixFTPEntryParser() { this(null); } /** * This constructor allows the creation of a UnixFTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public UnixFTPEntryParser(final FTPClientConfig config) { this(config, false); } /** * This constructor allows the creation of a UnixFTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @param trimLeadingSpaces if {@code true}, trim leading spaces from file names * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 3.4 */ public UnixFTPEntryParser(final FTPClientConfig config, final boolean trimLeadingSpaces) { super(REGEX); configure(config); this.trimLeadingSpaces = trimLeadingSpaces; } /** * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for this parser. */ @Override protected FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT); } /** * Parses a line of a unix (standard) FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the * file listing line doesn't describe a file, null is returned, otherwise a FTPFile instance representing the files in the * directory is returned. * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { final FTPFile file = new FTPFile(); file.setRawListing(entry); final int type; boolean isDevice = false; if (matches(entry)) { final String typeStr = group(1); final String hardLinkCount = group(15); final String usr = group(16); final String grp = group(17); final String filesize = group(18); final String datestr = group(19) + " " + group(20); String name = group(21); if (trimLeadingSpaces) { name = name.replaceFirst("^\\s+", ""); } try { if (group(19).contains(JA_MONTH)) { // special processing for Japanese format final FTPTimestampParserImpl jaParser = new FTPTimestampParserImpl(); jaParser.configure(new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT_JA, DEFAULT_RECENT_DATE_FORMAT_JA)); file.setTimestamp(jaParser.parseTimestamp(datestr)); } else { file.setTimestamp(super.parseTimestamp(datestr)); } } catch (final ParseException e) { // intentionally do nothing } // A 'whiteout' file is an ARTIFICIAL entry in any of several types of // 'translucent' filesystems, of which a 'union' filesystem is one. // bcdelfmpSs- switch (typeStr.charAt(0)) { case 'd': type = FTPFile.DIRECTORY_TYPE; break; case 'e': // NET-39 => z/OS external link type = FTPFile.SYMBOLIC_LINK_TYPE; break; case 'l': type = FTPFile.SYMBOLIC_LINK_TYPE; break; case 'b': case 'c': isDevice = true; type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented break; case 'f': case '-': type = FTPFile.FILE_TYPE; break; default: // e.g. ? and w = whiteout type = FTPFile.UNKNOWN_TYPE; } file.setType(type); int g = 4; for (int access = 0; access < 3; access++, g += 4) { // Use != '-' to avoid having to check for suid and sticky bits file.setPermission(access, FTPFile.READ_PERMISSION, !group(g).equals("-")); file.setPermission(access, FTPFile.WRITE_PERMISSION, !group(g + 1).equals("-")); final String execPerm = group(g + 2); file.setPermission(access, FTPFile.EXECUTE_PERMISSION, !execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))); } if (!isDevice) { try { file.setHardLinkCount(Integer.parseInt(hardLinkCount)); } catch (final NumberFormatException e) { // intentionally do nothing } } file.setUser(usr); file.setGroup(grp); try { file.setSize(Long.parseLong(filesize)); } catch (final NumberFormatException e) { // intentionally do nothing } // oddball cases like symbolic links, file names // with spaces in them. if (type == FTPFile.SYMBOLIC_LINK_TYPE) { final int end = name.indexOf(" -> "); // Give up if no link indicator is present if (end == -1) { file.setName(name); } else { file.setName(name.substring(0, end)); file.setLink(name.substring(end + 4)); } } else { file.setName(name); } return file; } return null; } /** * Preparse the list to discard "total nnn" lines */ @Override public List preParse(final List original) { // NET-389 original.removeIf(entry -> entry.matches("^total \\d+$")); return original; } } VMSFTPEntryParser.java000066400000000000000000000216341434047722200340060ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.io.BufferedReader; import java.io.IOException; import java.text.ParseException; import java.util.StringTokenizer; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; /** * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems. This is a sample of VMS LIST output * *

 *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
 *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
 *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
 * 
*

* Note: VMSFTPEntryParser can only be instantiated through the DefaultFTPParserFactory by classname. It will not be chosen by the autodetection scheme. *

* * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory */ public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { private static final String DEFAULT_DATE_FORMAT = "d-MMM-yyyy HH:mm:ss"; // 9-NOV-2001 12:30:24 /** * this is the regular expression used by this parser. */ private static final String REGEX = "(.*?;[0-9]+)\\s*" // 1 file and version + "(\\d+)(?:/\\d+)?\\s*" // 2 size/allocated + "(\\S+)\\s+(\\S+)\\s+" // 3+4 date and time + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" // 5(6,7,8) owner + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)"; // 9,10,11 Permissions (O,G,W) // TODO - perhaps restrict permissions to [RWED]* ? /** * Constructor for a VMSFTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public VMSFTPEntryParser() { this(null); } /** * This constructor allows the creation of a VMSFTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public VMSFTPEntryParser(final FTPClientConfig config) { super(REGEX); configure(config); } /** * Defines a default configuration to be used when this class is instantiated without a {@link FTPClientConfig FTPClientConfig} parameter being specified. * * @return the default configuration for this parser. */ @Override protected FTPClientConfig getDefaultConfiguration() { return new FTPClientConfig(FTPClientConfig.SYST_VMS, DEFAULT_DATE_FORMAT, null); } protected boolean isVersioning() { return false; } /** * DO NOT USE * * @param listStream the stream * @return the array of files * @throws IOException on error * @deprecated (2.2) No other FTPFileEntryParser implementations have this method. */ @Deprecated public FTPFile[] parseFileList(final java.io.InputStream listStream) throws IOException { final org.apache.commons.net.ftp.FTPListParseEngine engine = new org.apache.commons.net.ftp.FTPListParseEngine(this); engine.readServerList(listStream, null); return engine.getFiles(); } /** * Parses a line of a VMS FTP server file listing and converts it into a usable format in the form of an FTPFile instance. If the file * listing line doesn't describe a file, null is returned, otherwise a FTPFile instance representing the files in the * directory is returned. * * @param entry A line of text from the file listing * @return An FTPFile instance corresponding to the supplied entry */ @Override public FTPFile parseFTPEntry(final String entry) { // one block in VMS equals 512 bytes final long longBlock = 512; if (matches(entry)) { final FTPFile f = new FTPFile(); f.setRawListing(entry); String name = group(1); final String size = group(2); final String datestr = group(3) + " " + group(4); final String owner = group(5); final String permissions[] = new String[3]; permissions[0] = group(9); permissions[1] = group(10); permissions[2] = group(11); try { f.setTimestamp(super.parseTimestamp(datestr)); } catch (final ParseException e) { // intentionally do nothing } final String grp; final String user; final StringTokenizer t = new StringTokenizer(owner, ","); switch (t.countTokens()) { case 1: grp = null; user = t.nextToken(); break; case 2: grp = t.nextToken(); user = t.nextToken(); break; default: grp = null; user = null; } if (name.lastIndexOf(".DIR") != -1) { f.setType(FTPFile.DIRECTORY_TYPE); } else { f.setType(FTPFile.FILE_TYPE); } // set FTPFile name // Check also for versions to be returned or not if (!isVersioning()) { name = name.substring(0, name.lastIndexOf(';')); } f.setName(name); // size is retreived in blocks and needs to be put in bytes // for us humans and added to the FTPFile array final long sizeInBytes = Long.parseLong(size) * longBlock; f.setSize(sizeInBytes); f.setGroup(grp); f.setUser(user); // set group and owner // Set file permission. // VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain // R (read) W (write) E (execute) D (delete) // iterate for OWNER GROUP WORLD permissions for (int access = 0; access < 3; access++) { final String permission = permissions[access]; f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R') >= 0); f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W') >= 0); f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E') >= 0); } return f; } return null; } // DEPRECATED METHODS - for API compatibility only - DO NOT USE /** * Reads the next entry using the supplied BufferedReader object up to whatever delimits one entry from the next. This parser cannot use the default * implementation of simply calling BufferedReader.readLine(), because one entry may span multiple lines. * * @param reader The BufferedReader object from which entries are to be read. * * @return A string representing the next ftp entry or null if none found. * @throws IOException thrown on any IO Error reading from the reader. */ @Override public String readNextEntry(final BufferedReader reader) throws IOException { String line = reader.readLine(); final StringBuilder entry = new StringBuilder(); while (line != null) { if (line.startsWith("Directory") || line.startsWith("Total")) { line = reader.readLine(); continue; } entry.append(line); if (line.trim().endsWith(")")) { break; } line = reader.readLine(); } return entry.length() == 0 ? null : entry.toString(); } } VMSVersioningFTPEntryParser.java000066400000000000000000000133651434047722200360540ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.apache.commons.net.ftp.FTPClientConfig; /** * Special implementation VMSFTPEntryParser with versioning turned on. This parser removes all duplicates and only leaves the version with the highest version * number for each file name. *

* This is a sample of VMS LIST output *

* *
 *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
 *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
 *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
 * 
* * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) */ public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser { private static final String PRE_PARSE_REGEX = "(.*?);([0-9]+)\\s*.*"; private final Pattern preparsePattern; /** * Constructor for a VMSFTPEntryParser object. * * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. */ public VMSVersioningFTPEntryParser() { this(null); } /** * This constructor allows the creation of a VMSVersioningFTPEntryParser object with something other than the default configuration. * * @param config The {@link FTPClientConfig configuration} object used to configure this parser. * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. It it is seen, this is a * sign that REGEX is not a valid regular expression. * @since 1.4 */ public VMSVersioningFTPEntryParser(final FTPClientConfig config) { configure(config); try { // _preparse_matcher_ = new Perl5Matcher(); preparsePattern = Pattern.compile(PRE_PARSE_REGEX); } catch (final PatternSyntaxException pse) { throw new IllegalArgumentException("Unparseable regex supplied: " + PRE_PARSE_REGEX); } } @Override protected boolean isVersioning() { return true; } /** * Implement hook provided for those implementers (such as VMSVersioningFTPEntryParser, and possibly others) which return multiple files with the same name * to remove the duplicates .. * * @param original Original list * * @return Original list purged of duplicates */ @Override public List preParse(final List original) { final HashMap existingEntries = new HashMap<>(); final ListIterator iter = original.listIterator(); while (iter.hasNext()) { final String entry = iter.next().trim(); MatchResult result = null; final Matcher _preparse_matcher_ = preparsePattern.matcher(entry); if (_preparse_matcher_.matches()) { result = _preparse_matcher_.toMatchResult(); final String name = result.group(1); final String version = result.group(2); final Integer nv = Integer.valueOf(version); final Integer existing = existingEntries.get(name); if ((null != existing) && (nv.intValue() < existing.intValue())) { iter.remove(); // removes older version from original list. continue; } existingEntries.put(name, nv); } } // we've now removed all entries less than with less than the largest // version number for each name that were listed after the largest. // we now must remove those with smaller than the largest version number // for each name that were found before the largest while (iter.hasPrevious()) { final String entry = iter.previous().trim(); MatchResult result = null; final Matcher _preparse_matcher_ = preparsePattern.matcher(entry); if (_preparse_matcher_.matches()) { result = _preparse_matcher_.toMatchResult(); final String name = result.group(1); final String version = result.group(2); final int nv = Integer.parseInt(version); final Integer existing = existingEntries.get(name); if ((null != existing) && (nv < existing.intValue())) { iter.remove(); // removes older version from original list. } } } return original; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ftp/parser/package-info.java000066400000000000000000000015711434047722200331710ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * FTP file listing parser classes */ package org.apache.commons.net.ftp.parser;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/000077500000000000000000000000001434047722200266375ustar00rootroot00000000000000AuthenticatingIMAPClient.java000066400000000000000000000234671434047722200342140ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.imap; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.SSLContext; import org.apache.commons.net.util.Base64; /** * An IMAP Client class with authentication support. * * @see IMAPSClient */ public class AuthenticatingIMAPClient extends IMAPSClient { /** * The enumeration of currently-supported authentication methods. */ public enum AUTH_METHOD { /** The standarised (RFC4616) PLAIN method, which sends the password unencrypted (insecure). */ PLAIN("PLAIN"), /** The standarised (RFC2195) CRAM-MD5 method, which doesn't send the password (secure). */ CRAM_MD5("CRAM-MD5"), /** The unstandarised Microsoft LOGIN method, which sends the password unencrypted (insecure). */ LOGIN("LOGIN"), /** XOAUTH */ XOAUTH("XOAUTH"), /** XOAUTH 2 */ XOAUTH2("XOAUTH2"); private final String authName; AUTH_METHOD(final String name) { this.authName = name; } /** * Gets the name of the given authentication method suitable for the server. * * @return The name of the given authentication method suitable for the server. */ public final String getAuthName() { return authName; } } /** * Constructor for AuthenticatingIMAPClient that delegates to IMAPSClient. Sets security mode to explicit (isImplicit = false). */ public AuthenticatingIMAPClient() { this(DEFAULT_PROTOCOL, false); } /** * Constructor for AuthenticatingIMAPClient that delegates to IMAPSClient. * * @param implicit The security mode (Implicit/Explicit). */ public AuthenticatingIMAPClient(final boolean implicit) { this(DEFAULT_PROTOCOL, implicit); } /** * Constructor for AuthenticatingIMAPClient that delegates to IMAPSClient. * * @param implicit The security mode(Implicit/Explicit). * @param ctx A pre-configured SSL Context. */ public AuthenticatingIMAPClient(final boolean implicit, final SSLContext ctx) { this(DEFAULT_PROTOCOL, implicit, ctx); } /** * Constructor for AuthenticatingIMAPClient that delegates to IMAPSClient. * * @param context A pre-configured SSL Context. */ public AuthenticatingIMAPClient(final SSLContext context) { this(false, context); } /** * Constructor for AuthenticatingIMAPClient that delegates to IMAPSClient. * * @param proto the protocol. */ public AuthenticatingIMAPClient(final String proto) { this(proto, false); } /** * Constructor for AuthenticatingIMAPClient that delegates to IMAPSClient. * * @param proto the protocol. * @param implicit The security mode(Implicit/Explicit). */ public AuthenticatingIMAPClient(final String proto, final boolean implicit) { this(proto, implicit, null); } /** * Constructor for AuthenticatingIMAPClient that delegates to IMAPSClient. * * @param proto the protocol. * @param implicit The security mode(Implicit/Explicit). * @param ctx the context */ public AuthenticatingIMAPClient(final String proto, final boolean implicit, final SSLContext ctx) { super(proto, implicit, ctx); } /** * Authenticate to the IMAP server by sending the AUTHENTICATE command with the selected mechanism, using the given username and the given password. * * @param method the method name * @param username user * @param password password * @return True if successfully completed, false if not. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @throws NoSuchAlgorithmException If the CRAM hash algorithm cannot be instantiated by the Java runtime system. * @throws InvalidKeyException If the CRAM hash algorithm failed to use the given password. * @throws InvalidKeySpecException If the CRAM hash algorithm failed to use the given password. */ public boolean auth(final AuthenticatingIMAPClient.AUTH_METHOD method, final String username, final String password) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException { if (!IMAPReply.isContinuation(sendCommand(IMAPCommand.AUTHENTICATE, method.getAuthName()))) { return false; } switch (method) { case PLAIN: { // the server sends an empty response ("+ "), so we don't have to read it. final int result = sendData(Base64.encodeBase64StringUnChunked(("\000" + username + "\000" + password).getBytes(getCharset()))); if (result == IMAPReply.OK) { setState(IMAP.IMAPState.AUTH_STATE); } return result == IMAPReply.OK; } case CRAM_MD5: { // get the CRAM challenge (after "+ ") final byte[] serverChallenge = Base64.decodeBase64(getReplyString().substring(2).trim()); // get the Mac instance final Mac hmac_md5 = Mac.getInstance("HmacMD5"); hmac_md5.init(new SecretKeySpec(password.getBytes(getCharset()), "HmacMD5")); // compute the result: final byte[] hmacResult = convertToHexString(hmac_md5.doFinal(serverChallenge)).getBytes(getCharset()); // join the byte arrays to form the reply final byte[] usernameBytes = username.getBytes(getCharset()); final byte[] toEncode = new byte[usernameBytes.length + 1 /* the space */ + hmacResult.length]; System.arraycopy(usernameBytes, 0, toEncode, 0, usernameBytes.length); toEncode[usernameBytes.length] = ' '; System.arraycopy(hmacResult, 0, toEncode, usernameBytes.length + 1, hmacResult.length); // send the reply and read the server code: final int result = sendData(Base64.encodeBase64StringUnChunked(toEncode)); if (result == IMAPReply.OK) { setState(IMAP.IMAPState.AUTH_STATE); } return result == IMAPReply.OK; } case LOGIN: { // the server sends fixed responses (base64("Username") and // base64("Password")), so we don't have to read them. if (sendData(Base64.encodeBase64StringUnChunked(username.getBytes(getCharset()))) != IMAPReply.CONT) { return false; } final int result = sendData(Base64.encodeBase64StringUnChunked(password.getBytes(getCharset()))); if (result == IMAPReply.OK) { setState(IMAP.IMAPState.AUTH_STATE); } return result == IMAPReply.OK; } case XOAUTH: case XOAUTH2: { final int result = sendData(username); if (result == IMAPReply.OK) { setState(IMAP.IMAPState.AUTH_STATE); } return result == IMAPReply.OK; } } return false; // safety check } /** * Authenticate to the IMAP server by sending the AUTHENTICATE command with the selected mechanism, using the given username and the given password. * * @param method the method name * @param username user * @param password password * @return True if successfully completed, false if not. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @throws NoSuchAlgorithmException If the CRAM hash algorithm cannot be instantiated by the Java runtime system. * @throws InvalidKeyException If the CRAM hash algorithm failed to use the given password. * @throws InvalidKeySpecException If the CRAM hash algorithm failed to use the given password. */ public boolean authenticate(final AuthenticatingIMAPClient.AUTH_METHOD method, final String username, final String password) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException { return auth(method, username, password); } /** * Converts the given byte array to a String containing the hex values of the bytes. For example, the byte 'A' will be converted to '41', because this is * the ASCII code (and the byte value) of the capital letter 'A'. * * @param a The byte array to convert. * @return The resulting String of hex codes. */ private String convertToHexString(final byte[] a) { final StringBuilder result = new StringBuilder(a.length * 2); for (final byte element : a) { if ((element & 0x0FF) <= 15) { result.append("0"); } result.append(Integer.toHexString(element & 0x0FF)); } return result.toString(); } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/IMAP.java000066400000000000000000000374561434047722200302470ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.imap; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.List; import org.apache.commons.net.SocketClient; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.util.NetConstants; /** * The IMAP class provides the basic the functionality necessary to implement your own IMAP client. */ public class IMAP extends SocketClient { /** * Implement this interface and register it via {@link #setChunkListener(IMAPChunkListener)} in order to get access to multi-line partial command responses. * Useful when processing large FETCH responses. */ public interface IMAPChunkListener { /** * Called when a multi-line partial response has been received. * * @param imap the instance, get the response by calling {@link #getReplyString()} or {@link #getReplyStrings()} * @return {@code true} if the reply buffer is to be cleared on return */ boolean chunkReceived(IMAP imap); } public enum IMAPState { /** A constant representing the state where the client is not yet connected to a server. */ DISCONNECTED_STATE, /** A constant representing the "not authenticated" state. */ NOT_AUTH_STATE, /** A constant representing the "authenticated" state. */ AUTH_STATE, /** A constant representing the "logout" state. */ LOGOUT_STATE } /** The default IMAP port (RFC 3501). */ public static final int DEFAULT_PORT = 143; // RFC 3501, section 5.1.3. It should be "modified UTF-7". /** * The default control socket encoding. */ protected static final String __DEFAULT_ENCODING = "ISO-8859-1"; /** *

* Implementation of IMAPChunkListener that returns {@code true} but otherwise does nothing. *

*

* This is intended for use with a suitable ProtocolCommandListener. If the IMAP response contains multiple-line data, the protocol listener will be called * for each multi-line chunk. The accumulated reply data will be cleared after calling the listener. If the response is very long, this can significantly * reduce memory requirements. The listener will also start receiving response data earlier, as it does not have to wait for the entire response to be read. *

*

* The ProtocolCommandListener must be prepared to accept partial responses. This should not be a problem for listeners that just log the input. *

* * @see #setChunkListener(IMAPChunkListener) * @since 3.4 */ public static final IMAPChunkListener TRUE_CHUNK_LISTENER = imap -> true; /** * Quote an input string if necessary. If the string is enclosed in double-quotes it is assumed to be quoted already and is returned unchanged. If it is the * empty string, "" is returned. If it contains a space then it is enclosed in double quotes, escaping the characters backslash and double-quote. * * @param input the value to be quoted, may be null * @return the quoted value */ static String quoteMailboxName(final String input) { if (input == null) { // Don't throw NPE here return null; } if (input.isEmpty()) { return "\"\""; // return the string "" } // Length check is necessary to ensure a lone double-quote is quoted if (input.length() > 1 && input.startsWith("\"") && input.endsWith("\"")) { return input; // Assume already quoted } if (input.contains(" ")) { // quoted strings must escape \ and " return "\"" + input.replaceAll("([\\\\\"])", "\\\\$1") + "\""; } return input; } private IMAPState state; protected BufferedWriter __writer; protected BufferedReader _reader; private int replyCode; private final List replyLines; private volatile IMAPChunkListener chunkListener; private final char[] initialID = { 'A', 'A', 'A', 'A' }; /** * The default IMAPClient constructor. Initializes the state to DISCONNECTED_STATE. */ public IMAP() { setDefaultPort(DEFAULT_PORT); state = IMAPState.DISCONNECTED_STATE; _reader = null; __writer = null; replyLines = new ArrayList<>(); createCommandSupport(); } /** * Performs connection initialization and sets state to {@link IMAPState#NOT_AUTH_STATE}. */ @Override protected void _connectAction_() throws IOException { super._connectAction_(); _reader = new CRLFLineReader(new InputStreamReader(_input_, __DEFAULT_ENCODING)); __writer = new BufferedWriter(new OutputStreamWriter(_output_, __DEFAULT_ENCODING)); final int tmo = getSoTimeout(); if (tmo <= 0) { // none set currently setSoTimeout(connectTimeout); // use connect timeout to ensure we don't block forever } getReply(false); // untagged response if (tmo <= 0) { setSoTimeout(tmo); // restore the original value } setState(IMAPState.NOT_AUTH_STATE); } /** * Disconnects the client from the server, and sets the state to DISCONNECTED_STATE . The reply text information from the last issued command * is voided to allow garbage collection of the memory used to store that information. * * @throws IOException If there is an error in disconnecting. */ @Override public void disconnect() throws IOException { super.disconnect(); _reader = null; __writer = null; replyLines.clear(); setState(IMAPState.DISCONNECTED_STATE); } /** * Sends a command to the server and return whether successful. * * @param command The IMAP command to send (one of the IMAPCommand constants). * @return {@code true} if the command was successful * @throws IOException on error */ public boolean doCommand(final IMAPCommand command) throws IOException { return IMAPReply.isSuccess(sendCommand(command)); } /** * Sends a command and arguments to the server and return whether successful. * * @param command The IMAP command to send (one of the IMAPCommand constants). * @param args The command arguments. * @return {@code true} if the command was successful * @throws IOException on error */ public boolean doCommand(final IMAPCommand command, final String args) throws IOException { return IMAPReply.isSuccess(sendCommand(command, args)); } /** * Overrides {@link SocketClient#fireReplyReceived(int, String)} so as to avoid creating the reply string if there are no listeners to invoke. * * @param replyCode passed to the listeners * @param ignored the string is only created if there are listeners defined. * @see #getReplyString() * @since 3.4 */ @Override protected void fireReplyReceived(final int replyCode, final String ignored) { if (getCommandSupport().getListenerCount() > 0) { getCommandSupport().fireReplyReceived(replyCode, getReplyString()); } } /** * Generates a new command ID (tag) for a command. * * @return a new command ID (tag) for an IMAP command. */ protected String generateCommandID() { final String res = new String(initialID); // "increase" the ID for the next call boolean carry = true; // want to increment initially for (int i = initialID.length - 1; carry && i >= 0; i--) { if (initialID[i] == 'Z') { initialID[i] = 'A'; } else { initialID[i]++; carry = false; // did not wrap round } } return res; } /** * Get the reply for a command that expects a tagged response. * * @throws IOException */ private void getReply() throws IOException { getReply(true); // tagged response } /** * Get the reply for a command, reading the response until the reply is found. * * @param wantTag {@code true} if the command expects a tagged response. * @throws IOException */ private void getReply(final boolean wantTag) throws IOException { replyLines.clear(); String line = _reader.readLine(); if (line == null) { throw new EOFException("Connection closed without indication."); } replyLines.add(line); if (wantTag) { while (IMAPReply.isUntagged(line)) { int literalCount = IMAPReply.literalCount(line); final boolean isMultiLine = literalCount >= 0; while (literalCount >= 0) { line = _reader.readLine(); if (line == null) { throw new EOFException("Connection closed without indication."); } replyLines.add(line); literalCount -= line.length() + 2; // Allow for CRLF } if (isMultiLine) { final IMAPChunkListener il = chunkListener; if (il != null) { final boolean clear = il.chunkReceived(this); if (clear) { fireReplyReceived(IMAPReply.PARTIAL, getReplyString()); replyLines.clear(); } } } line = _reader.readLine(); // get next chunk or final tag if (line == null) { throw new EOFException("Connection closed without indication."); } replyLines.add(line); } // check the response code on the last line replyCode = IMAPReply.getReplyCode(line); } else { replyCode = IMAPReply.getUntaggedReplyCode(line); } fireReplyReceived(replyCode, getReplyString()); } /** * Returns the reply to the last command sent to the server. The value is a single string containing all the reply lines including newlines. * * @return The last server response. */ public String getReplyString() { final StringBuilder buffer = new StringBuilder(256); for (final String s : replyLines) { buffer.append(s); buffer.append(SocketClient.NETASCII_EOL); } return buffer.toString(); } /** * Returns an array of lines received as a reply to the last command sent to the server. The lines have end of lines truncated. * * @return The last server response. */ public String[] getReplyStrings() { return replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY); } /** * Returns the current IMAP client state. * * @return The current IMAP client state. */ public IMAP.IMAPState getState() { return state; } /** * Sends a command with no arguments to the server and returns the reply code. * * @param command The IMAP command to send (one of the IMAPCommand constants). * @return The server reply code (see IMAPReply). * @throws IOException on error **/ public int sendCommand(final IMAPCommand command) throws IOException { return sendCommand(command, null); } /** * Sends a command and arguments to the server and returns the reply code. * * @param command The IMAP command to send (one of the IMAPCommand constants). * @param args The command arguments. * @return The server reply code (see IMAPReply). * @throws IOException on error */ public int sendCommand(final IMAPCommand command, final String args) throws IOException { return sendCommand(command.getIMAPCommand(), args); } /** * Sends a command with no arguments to the server and returns the reply code. * * @param command The IMAP command to send. * @return The server reply code (see IMAPReply). * @throws IOException on error */ public int sendCommand(final String command) throws IOException { return sendCommand(command, null); } /** * Sends a command an arguments to the server and returns the reply code. * * @param command The IMAP command to send. * @param args The command arguments. * @return The server reply code (see IMAPReply). * @throws IOException on error */ public int sendCommand(final String command, final String args) throws IOException { return sendCommandWithID(generateCommandID(), command, args); } /** * Sends a command an arguments to the server and returns the reply code. * * @param commandID The ID (tag) of the command. * @param command The IMAP command to send. * @param args The command arguments. * @return The server reply code (either IMAPReply.OK, IMAPReply.NO or IMAPReply.BAD). */ private int sendCommandWithID(final String commandID, final String command, final String args) throws IOException { final StringBuilder __commandBuffer = new StringBuilder(); if (commandID != null) { __commandBuffer.append(commandID); __commandBuffer.append(' '); } __commandBuffer.append(command); if (args != null) { __commandBuffer.append(' '); __commandBuffer.append(args); } __commandBuffer.append(SocketClient.NETASCII_EOL); final String message = __commandBuffer.toString(); __writer.write(message); __writer.flush(); fireCommandSent(command, message); getReply(); return replyCode; } /** * Sends data to the server and returns the reply code. * * @param command The IMAP command to send. * @return The server reply code (see IMAPReply). * @throws IOException on error */ public int sendData(final String command) throws IOException { return sendCommandWithID(null, command, null); } /** * Sets the current chunk listener. If a listener is registered and the implementation returns true, then any registered * {@link org.apache.commons.net.PrintCommandListener PrintCommandListener} instances will be invoked with the partial response and a status of * {@link IMAPReply#PARTIAL} to indicate that the final reply code is not yet known. * * @param listener the class to use, or {@code null} to disable * @see #TRUE_CHUNK_LISTENER * @since 3.4 */ public void setChunkListener(final IMAPChunkListener listener) { chunkListener = listener; } /** * Sets IMAP client state. This must be one of the _STATE constants. * * @param state The new state. */ protected void setState(final IMAP.IMAPState state) { this.state = state; } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/IMAPClient.java000066400000000000000000000535331434047722200314000ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.imap; import java.io.IOException; /** * The IMAPClient class provides the basic functionalities found in an IMAP client. */ public class IMAPClient extends IMAP { /** * The message data item names for the FETCH command defined in RFC 3501. */ public enum FETCH_ITEM_NAMES { /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE). */ ALL, /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE). */ FAST, /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY). */ FULL, /** Non-extensible form of BODYSTRUCTURE or the text of a particular body section. */ BODY, /** The [MIME-IMB] body structure of the message. */ BODYSTRUCTURE, /** The envelope structure of the message. */ ENVELOPE, /** The flags that are set for this message. */ FLAGS, /** The internal date of the message. */ INTERNALDATE, /** A prefix for RFC-822 item names. */ RFC822, /** The unique identifier for the message. */ UID } /** * The search criteria defined in RFC 3501. */ public enum SEARCH_CRITERIA { /** All messages in the mailbox. */ ALL, /** Messages with the \Answered flag set. */ ANSWERED, /** * Messages that contain the specified string in the envelope structure's BCC field. */ BCC, /** * Messages whose internal date (disregarding time and time zone) is earlier than the specified date. */ BEFORE, /** * Messages that contain the specified string in the body of the message. */ BODY, /** * Messages that contain the specified string in the envelope structure's CC field. */ CC, /** Messages with the \Deleted flag set. */ DELETED, /** Messages with the \Draft flag set. */ DRAFT, /** Messages with the \Flagged flag set. */ FLAGGED, /** * Messages that contain the specified string in the envelope structure's FROM field. */ FROM, /** * Messages that have a header with the specified field-name (as defined in [RFC-2822]) and that contains the specified string in the text of the header * (what comes after the colon). If the string to search is zero-length, this matches all messages that have a header line with the specified field-name * regardless of the contents. */ HEADER, /** Messages with the specified keyword flag set. */ KEYWORD, /** * Messages with an [RFC-2822] size larger than the specified number of octets. */ LARGER, /** * Messages that have the \Recent flag set but not the \Seen flag. This is functionally equivalent to "(RECENT UNSEEN)". */ NEW, /** Messages that do not match the specified search key. */ NOT, /** * Messages that do not have the \Recent flag set. This is functionally equivalent to "NOT RECENT" (as opposed to "NOT NEW"). */ OLD, /** * Messages whose internal date (disregarding time and time zone) is within the specified date. */ ON, /** Messages that match either search key. */ OR, /** Messages that have the \Recent flag set. */ RECENT, /** Messages that have the \Seen flag set. */ SEEN, /** * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is earlier than the specified date. */ SENTBEFORE, /** * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is within the specified date. */ SENTON, /** * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is within or later than the specified date. */ SENTSINCE, /** * Messages whose internal date (disregarding time and time zone) is within or later than the specified date. */ SINCE, /** * Messages with an [RFC-2822] size smaller than the specified number of octets. */ SMALLER, /** * Messages that contain the specified string in the envelope structure's SUBJECT field. */ SUBJECT, /** * Messages that contain the specified string in the header or body of the message. */ TEXT, /** * Messages that contain the specified string in the envelope structure's TO field. */ TO, /** * Messages with unique identifiers corresponding to the specified unique identifier set. Sequence set ranges are permitted. */ UID, /** Messages that do not have the \Answered flag set. */ UNANSWERED, /** Messages that do not have the \Deleted flag set. */ UNDELETED, /** Messages that do not have the \Draft flag set. */ UNDRAFT, /** Messages that do not have the \Flagged flag set. */ UNFLAGGED, /** Messages that do not have the specified keyword flag set. */ UNKEYWORD, /** Messages that do not have the \Seen flag set. */ UNSEEN } // --------- commands available in all states /** * The status data items defined in RFC 3501. */ public enum STATUS_DATA_ITEMS { /** The number of messages in the mailbox. */ MESSAGES, /** The number of messages with the \Recent flag set. */ RECENT, /** The next unique identifier value of the mailbox. */ UIDNEXT, /** The unique identifier validity value of the mailbox. */ UIDVALIDITY, /** The number of messages which do not have the \Seen flag set. */ UNSEEN } private static final char DQUOTE = '"'; private static final String DQUOTE_S = "\""; // --------- commands available in the not-authenticated state // STARTTLS skipped - see IMAPSClient. // AUTHENTICATE skipped - see AuthenticatingIMAPClient. /** * Send an APPEND command to the server. * * @param mailboxName The mailbox name. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead. */ @Deprecated public boolean append(final String mailboxName) throws IOException { return append(mailboxName, null, null); } // --------- commands available in the authenticated state /** * Send an APPEND command to the server. * * @param mailboxName The mailbox name. * @param flags The flag parenthesized list (optional). * @param datetime The date/time string (optional). * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead. */ @Deprecated public boolean append(final String mailboxName, final String flags, final String datetime) throws IOException { String args = mailboxName; if (flags != null) { args += " " + flags; } if (datetime != null) { if (datetime.charAt(0) == '{') { args += " " + datetime; } else { args += " {" + datetime + "}"; } } return doCommand(IMAPCommand.APPEND, args); } /** * Send an APPEND command to the server. * * @param mailboxName The mailbox name. * @param flags The flag parenthesized list (optional). * @param datetime The date/time string (optional). * @param message The message to append. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. * @since 3.4 */ public boolean append(final String mailboxName, final String flags, final String datetime, final String message) throws IOException { final StringBuilder args = new StringBuilder(quoteMailboxName(mailboxName)); if (flags != null) { args.append(" ").append(flags); } if (datetime != null) { args.append(" "); if (datetime.charAt(0) == DQUOTE) { args.append(datetime); } else { args.append(DQUOTE).append(datetime).append(DQUOTE); } } args.append(" "); // String literal (probably not used much - if at all) if (message.startsWith(DQUOTE_S) && message.endsWith(DQUOTE_S)) { args.append(message); return doCommand(IMAPCommand.APPEND, args.toString()); } args.append('{').append(message.getBytes(IMAP.__DEFAULT_ENCODING).length).append('}'); // length of message final int status = sendCommand(IMAPCommand.APPEND, args.toString()); return IMAPReply.isContinuation(status) // expecting continuation response && IMAPReply.isSuccess(sendData(message)); // if so, send the data } /** * Send a CAPABILITY command to the server. * * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs */ public boolean capability() throws IOException { return doCommand(IMAPCommand.CAPABILITY); } /** * Send a CHECK command to the server. * * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean check() throws IOException { return doCommand(IMAPCommand.CHECK); } /** * Send a CLOSE command to the server. * * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean close() throws IOException { return doCommand(IMAPCommand.CLOSE); } /** * Send a COPY command to the server. * * @param sequenceSet The sequence set to fetch. * @param mailboxName The mailbox name. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean copy(final String sequenceSet, final String mailboxName) throws IOException { return doCommand(IMAPCommand.COPY, sequenceSet + " " + quoteMailboxName(mailboxName)); } /** * Send a CREATE command to the server. * * @param mailboxName The mailbox name to create. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean create(final String mailboxName) throws IOException { return doCommand(IMAPCommand.CREATE, quoteMailboxName(mailboxName)); } /** * Send a DELETE command to the server. * * @param mailboxName The mailbox name to delete. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean delete(final String mailboxName) throws IOException { return doCommand(IMAPCommand.DELETE, quoteMailboxName(mailboxName)); } /** * Send an EXAMINE command to the server. * * @param mailboxName The mailbox name to examine. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean examine(final String mailboxName) throws IOException { return doCommand(IMAPCommand.EXAMINE, quoteMailboxName(mailboxName)); } /** * Send an EXPUNGE command to the server. * * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean expunge() throws IOException { return doCommand(IMAPCommand.EXPUNGE); } /** * Send a FETCH command to the server. * * @param sequenceSet The sequence set to fetch (e.g. 1:4,6,11,100:*) * @param itemNames The item names for the FETCH command. (e.g. BODY.PEEK[HEADER.FIELDS (SUBJECT)]) If multiple item names are requested, these must be * enclosed in parentheses, e.g. "(UID FLAGS BODY.PEEK[])" * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. * @see #getReplyString() * @see #getReplyStrings() */ public boolean fetch(final String sequenceSet, final String itemNames) throws IOException { return doCommand(IMAPCommand.FETCH, sequenceSet + " " + itemNames); } /** * Send a LIST command to the server. Quotes the parameters if necessary. * * @param refName The reference name If empty, indicates that the mailbox name is interpreted as by SELECT. * @param mailboxName The mailbox name. If empty, this is a special request to return the hierarchy delimiter and the root name of the name given in the * reference * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean list(final String refName, final String mailboxName) throws IOException { return doCommand(IMAPCommand.LIST, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName)); } /** * Login to the IMAP server with the given username and password. You must first connect to the server with * {@link org.apache.commons.net.SocketClient#connect connect } before attempting to login. A login attempt is only valid if the client is in the * NOT_AUTH_STATE. After logging in, the client enters the AUTH_STATE. * * @param username The account name being logged in to. * @param password The plain text password of the account. * @return True if the login attempt was successful, false if not. * @throws IOException If a network I/O error occurs in the process of logging in. */ public boolean login(final String username, final String password) throws IOException { if (getState() != IMAP.IMAPState.NOT_AUTH_STATE) { return false; } if (!doCommand(IMAPCommand.LOGIN, username + " " + password)) { return false; } setState(IMAP.IMAPState.AUTH_STATE); return true; } // --------- commands available in the selected state /** * Send a LOGOUT command to the server. To fully disconnect from the server you must call disconnect(). A logout attempt is valid in any state. If the * client is in the not authenticated or authenticated state, it enters the logout on a successful logout. * * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean logout() throws IOException { return doCommand(IMAPCommand.LOGOUT); } /** * Send an LSUB command to the server. Quotes the parameters if necessary. * * @param refName The reference name. * @param mailboxName The mailbox name. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean lsub(final String refName, final String mailboxName) throws IOException { return doCommand(IMAPCommand.LSUB, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName)); } /** * Send a NOOP command to the server. This is useful for keeping a connection alive since most IMAP servers will timeout after 10 minutes of inactivity. * * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean noop() throws IOException { return doCommand(IMAPCommand.NOOP); } /** * Send a RENAME command to the server. * * @param oldMailboxName The existing mailbox name to rename. * @param newMailboxName The new mailbox name. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean rename(final String oldMailboxName, final String newMailboxName) throws IOException { return doCommand(IMAPCommand.RENAME, quoteMailboxName(oldMailboxName) + " " + quoteMailboxName(newMailboxName)); } /** * Send a SEARCH command to the server. * * @param criteria The search criteria. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean search(final String criteria) throws IOException { return search(null, criteria); } /** * Send a SEARCH command to the server. * * @param charset The charset (optional). * @param criteria The search criteria. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean search(final String charset, final String criteria) throws IOException { String args = ""; if (charset != null) { args += "CHARSET " + charset; } args += criteria; return doCommand(IMAPCommand.SEARCH, args); } /** * Send a SELECT command to the server. * * @param mailboxName The mailbox name to select. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean select(final String mailboxName) throws IOException { return doCommand(IMAPCommand.SELECT, quoteMailboxName(mailboxName)); } /** * Send a STATUS command to the server. * * @param mailboxName The reference name. * @param itemNames The status data item names. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean status(final String mailboxName, final String[] itemNames) throws IOException { if (itemNames == null || itemNames.length < 1) { throw new IllegalArgumentException("STATUS command requires at least one data item name"); } final StringBuilder sb = new StringBuilder(); sb.append(quoteMailboxName(mailboxName)); sb.append(" ("); for (int i = 0; i < itemNames.length; i++) { if (i > 0) { sb.append(" "); } sb.append(itemNames[i]); } sb.append(")"); return doCommand(IMAPCommand.STATUS, sb.toString()); } /** * Send a STORE command to the server. * * @param sequenceSet The sequence set to update (e.g. 2:5) * @param itemNames The item name for the STORE command (i.e. [+|-]FLAGS[.SILENT]) * @param itemValues The item values for the STORE command. (e.g. (\Deleted) ) * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean store(final String sequenceSet, final String itemNames, final String itemValues) throws IOException { return doCommand(IMAPCommand.STORE, sequenceSet + " " + itemNames + " " + itemValues); } /** * Send a SUBSCRIBE command to the server. * * @param mailboxName The mailbox name to subscribe to. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean subscribe(final String mailboxName) throws IOException { return doCommand(IMAPCommand.SUBSCRIBE, quoteMailboxName(mailboxName)); } /** * Send a UID command to the server. * * @param command The command for UID. * @param commandArgs The arguments for the command. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean uid(final String command, final String commandArgs) throws IOException { return doCommand(IMAPCommand.UID, command + " " + commandArgs); } /** * Send a UNSUBSCRIBE command to the server. * * @param mailboxName The mailbox name to unsubscribe from. * @return {@code true} if the command was successful,{@code false} if not. * @throws IOException If a network I/O error occurs. */ public boolean unsubscribe(final String mailboxName) throws IOException { return doCommand(IMAPCommand.UNSUBSCRIBE, quoteMailboxName(mailboxName)); } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/IMAPCommand.java000066400000000000000000000061511434047722200315320ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.imap; /** * IMAPCommand stores IMAP command codes. */ public enum IMAPCommand { // These enums must either use the same name as the IMAP command // or must provide the correct string as the parameter. // Commands valid in any state: CAPABILITY(0), NOOP(0), LOGOUT(0), // Commands valid in Not Authenticated state STARTTLS(0), AUTHENTICATE(1), LOGIN(2), XOAUTH(1), // commands valid in authenticated state SELECT(1), EXAMINE(1), CREATE(1), DELETE(1), RENAME(2), SUBSCRIBE(1), UNSUBSCRIBE(1), LIST(2), LSUB(2), STATUS(2), // P2 = list in () APPEND(2, 4), // mbox [(flags)] [date-time] literal // commands valid in selected state (substate of authenticated) CHECK(0), CLOSE(0), EXPUNGE(0), SEARCH(1, Integer.MAX_VALUE), FETCH(2), STORE(3), COPY(2), UID(2, Integer.MAX_VALUE),; /** * Get the IMAP protocol string command corresponding to a command code. * * @param command the IMAPCommand whose command string is required. * @return The IMAP protocol string command corresponding to a command code. */ public static final String getCommand(final IMAPCommand command) { return command.getIMAPCommand(); } private final String imapCommand; @SuppressWarnings("unused") // not yet used private final int minParamCount; @SuppressWarnings("unused") // not yet used private final int maxParamCount; IMAPCommand() { this(null); } IMAPCommand(final int paramCount) { this(null, paramCount, paramCount); } IMAPCommand(final int minCount, final int maxCount) { this(null, minCount, maxCount); } IMAPCommand(final String name) { this(name, 0); } IMAPCommand(final String name, final int paramCount) { this(name, paramCount, paramCount); } IMAPCommand(final String name, final int minCount, final int maxCount) { this.imapCommand = name; this.minParamCount = minCount; this.maxParamCount = maxCount; } /** * Get the IMAP protocol string command for this command * * @return The IMAP protocol string command corresponding to this command */ public String getIMAPCommand() { return imapCommand != null ? imapCommand : name(); } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/IMAPReply.java000066400000000000000000000142301434047722200312440ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.imap; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.net.MalformedServerReplyException; /** * IMAPReply stores IMAP reply code constants. */ public final class IMAPReply { /** The reply code indicating success of an operation. */ public static final int OK = 0; /** The reply code indicating failure of an operation. */ public static final int NO = 1; /** The reply code indicating command rejection. */ public static final int BAD = 2; /** The reply code indicating command continuation. */ public static final int CONT = 3; /** * The reply code indicating a partial response. This is used when a chunk listener is registered and the listener requests that the reply lines are cleared * on return. * * @since 3.4 */ public static final int PARTIAL = 3; /** The IMAP reply String indicating success of an operation. */ private static final String IMAP_OK = "OK"; /** The IMAP reply String indicating failure of an operation. */ private static final String IMAP_NO = "NO"; /** The IMAP reply String indicating command rejection. */ private static final String IMAP_BAD = "BAD"; // Start of line for untagged replies private static final String IMAP_UNTAGGED_PREFIX = "* "; // Start of line for continuation replies private static final String IMAP_CONTINUATION_PREFIX = "+"; private static final String TAGGED_RESPONSE = "^\\w+ (\\S+).*"; // TODO perhaps be less strict on tag match? // tag cannot contain: + ( ) { SP CTL % * " \ ] private static final Pattern TAGGED_PATTERN = Pattern.compile(TAGGED_RESPONSE); private static final String UNTAGGED_RESPONSE = "^\\* (\\S+).*"; private static final Pattern UNTAGGED_PATTERN = Pattern.compile(UNTAGGED_RESPONSE); private static final Pattern LITERAL_PATTERN = Pattern.compile("\\{(\\d+)\\}$"); // {dd} /** * Intepret the String reply code - OK, NO, BAD - in a tagged response as a integer. * * @param line the tagged line to be checked * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} * @throws IOException if the input has an unexpected format */ public static int getReplyCode(final String line) throws IOException { return getReplyCode(line, TAGGED_PATTERN); } // Helper method to process both tagged and untagged replies. private static int getReplyCode(final String line, final Pattern pattern) throws IOException { if (isContinuation(line)) { return CONT; } final Matcher m = pattern.matcher(line); if (m.matches()) { // TODO would lookingAt() be more efficient? If so, then drop trailing .* from patterns final String code = m.group(1); if (code.equals(IMAP_OK)) { return OK; } if (code.equals(IMAP_BAD)) { return BAD; } if (code.equals(IMAP_NO)) { return NO; } } throw new MalformedServerReplyException("Received unexpected IMAP protocol response from server: '" + line + "'."); } /** * Intepret the String reply code - OK, NO, BAD - in an untagged response as a integer. * * @param line the untagged line to be checked * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT} * @throws IOException if the input has an unexpected format */ public static int getUntaggedReplyCode(final String line) throws IOException { return getReplyCode(line, UNTAGGED_PATTERN); } /** * Checks if the reply line is a continuation, i.e. starts with "+" * * @param replyCode the code to be checked * @return {@code true} if the response was a continuation */ public static boolean isContinuation(final int replyCode) { return replyCode == CONT; } /** * Checks if the reply line is a continuation, i.e. starts with "+" * * @param line the line to be checked * @return {@code true} if the line is untagged */ public static boolean isContinuation(final String line) { return line.startsWith(IMAP_CONTINUATION_PREFIX); } /** * Checks whether the reply code indicates success or not * * @param replyCode the code to check * @return {@code true} if the code equals {@link #OK} */ public static boolean isSuccess(final int replyCode) { return replyCode == OK; } /** * Checks if the reply line is untagged - e.g. "* OK ..." * * @param line to be checked * @return {@code true} if the line is untagged */ public static boolean isUntagged(final String line) { return line.startsWith(IMAP_UNTAGGED_PREFIX); } /** * Checks if the line introduces a literal, i.e. ends with {dd} * * @param line the line to check * * @return the literal count, or -1 if there was no literal. */ public static int literalCount(final String line) { final Matcher m = LITERAL_PATTERN.matcher(line); if (m.find()) { return Integer.parseInt(m.group(1)); // Should always parse because we matched \d+ } return -1; } // Cannot be instantiated. private IMAPReply() { } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/IMAPSClient.java000066400000000000000000000307251434047722200315210ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.imap; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.util.SSLContextUtils; import org.apache.commons.net.util.SSLSocketUtils; /** * The IMAPSClient class provides SSL/TLS connection encryption to IMAPClient. Copied from * FTPSClient and modified to suit * IMAP. If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right after the connection has been established. In explicit mode (the * default), SSL/TLS negotiation starts when the user calls execTLS() and the server accepts the command. * *
 * {@code
 * //Implicit usage:
 *
 *               IMAPSClient c = new IMAPSClient(true);
 *               c.connect("127.0.0.1", 993);
 *
 * //Explicit usage:
 *
 *               IMAPSClient c = new IMAPSClient();
 *               c.connect("127.0.0.1", 143);
 *               if (c.execTLS()) { /rest of the commands here/ }
 * }
 * 
* * Warning: the hostname is not verified against the certificate by default, use {@link #setHostnameVerifier(HostnameVerifier)} or * {@link #setEndpointCheckingEnabled(boolean)} (on Java 1.7+) to enable verification. */ public class IMAPSClient extends IMAPClient { /** The default IMAP over SSL port. */ public static final int DEFAULT_IMAPS_PORT = 993; /** Default secure socket protocol name. */ public static final String DEFAULT_PROTOCOL = "TLS"; /** The security mode. True - Implicit Mode / False - Explicit Mode. */ private final boolean isImplicit; /** The secure socket protocol to be used, like SSL/TLS. */ private final String protocol; /** The context object. */ private SSLContext context; /** * The cipher suites. SSLSockets have a default set of these anyway, so no initialization required. */ private String[] suites; /** The protocol versions. */ private String[] protocols // null; ;// {"SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "SSLv2Hello"}; /** The IMAPS {@link TrustManager} implementation, default null. */ private TrustManager trustManager; /** The {@link KeyManager}, default null. */ private KeyManager keyManager; /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ private HostnameVerifier hostnameVerifier; /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */ private boolean tlsEndpointChecking; /** * Constructor for IMAPSClient. Sets security mode to explicit (isImplicit = false). */ public IMAPSClient() { this(DEFAULT_PROTOCOL, false); } /** * Constructor for IMAPSClient. * * @param implicit The security mode (Implicit/Explicit). */ public IMAPSClient(final boolean implicit) { this(DEFAULT_PROTOCOL, implicit); } /** * Constructor for IMAPSClient. * * @param implicit The security mode(Implicit/Explicit). * @param ctx A pre-configured SSL Context. */ public IMAPSClient(final boolean implicit, final SSLContext ctx) { this(DEFAULT_PROTOCOL, implicit, ctx); } /** * Constructor for IMAPSClient. * * @param context A pre-configured SSL Context. */ public IMAPSClient(final SSLContext context) { this(false, context); } /** * Constructor for IMAPSClient. * * @param proto the protocol. */ public IMAPSClient(final String proto) { this(proto, false); } /** * Constructor for IMAPSClient. * * @param proto the protocol. * @param implicit The security mode(Implicit/Explicit). */ public IMAPSClient(final String proto, final boolean implicit) { this(proto, implicit, null); } /** * Constructor for IMAPSClient. * * @param proto the protocol. * @param implicit The security mode(Implicit/Explicit). * @param ctx the SSL context */ public IMAPSClient(final String proto, final boolean implicit, final SSLContext ctx) { setDefaultPort(DEFAULT_IMAPS_PORT); protocol = proto; isImplicit = implicit; context = ctx; } /** * Because there are so many connect() methods, the _connectAction_() method is provided as a means of performing some action immediately after establishing * a connection, rather than reimplementing all of the connect() methods. * * @throws IOException If it is thrown by _connectAction_(). * @see org.apache.commons.net.SocketClient#_connectAction_() */ @Override protected void _connectAction_() throws IOException { // Implicit mode. if (isImplicit) { performSSLNegotiation(); } super._connectAction_(); // Explicit mode - don't do anything. The user calls execTLS() } /** * The TLS command execution. * * @throws SSLException If the server reply code is not positive. * @throws IOException If an I/O error occurs while sending the command or performing the negotiation. * @return TRUE if the command and negotiation succeeded. */ public boolean execTLS() throws SSLException, IOException { if (sendCommand(IMAPCommand.getCommand(IMAPCommand.STARTTLS)) != IMAPReply.OK) { return false; // throw new SSLException(getReplyString()); } performSSLNegotiation(); return true; } /** * Returns the names of the cipher suites which could be enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is not an * {@link SSLSocket} instance, returns null. * * @return An array of cipher suite names, or null. */ public String[] getEnabledCipherSuites() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledCipherSuites(); } return null; } /** * Returns the names of the protocol versions which are currently enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is * not an {@link SSLSocket} instance, returns null. * * @return An array of protocols, or null. */ public String[] getEnabledProtocols() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledProtocols(); } return null; } /** * Get the currently configured {@link HostnameVerifier}. * * @return A HostnameVerifier instance. * @since 3.4 */ public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } /** * Get the {@link KeyManager} instance. * * @return The current {@link KeyManager} instance. */ private KeyManager getKeyManager() { return keyManager; } /** * Get the currently configured {@link TrustManager}. * * @return A TrustManager instance. */ public TrustManager getTrustManager() { return trustManager; } /** * Performs a lazy init of the SSL context. * * @throws IOException When could not initialize the SSL context. */ private void initSSLContext() throws IOException { if (context == null) { context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); } } /** * Return whether or not endpoint identification using the HTTPS algorithm on Java 1.7+ is enabled. The default behavior is for this to be disabled. * * @return True if enabled, false if not. * @since 3.4 */ public boolean isEndpointCheckingEnabled() { return tlsEndpointChecking; } /** * SSL/TLS negotiation. Acquires an SSL socket of a connection and carries out handshake processing. * * @throws IOException If server negotiation fails. */ private void performSSLNegotiation() throws IOException { initSSLContext(); final SSLSocketFactory ssf = context.getSocketFactory(); final String host = _hostname_ != null ? _hostname_ : getRemoteAddress().getHostAddress(); final int port = getRemotePort(); final SSLSocket socket = (SSLSocket) ssf.createSocket(_socket_, host, port, true); socket.setEnableSessionCreation(true); socket.setUseClientMode(true); if (tlsEndpointChecking) { SSLSocketUtils.enableEndpointNameVerification(socket); } if (protocols != null) { socket.setEnabledProtocols(protocols); } if (suites != null) { socket.setEnabledCipherSuites(suites); } socket.startHandshake(); // TODO the following setup appears to duplicate that in the super class methods _socket_ = socket; _input_ = socket.getInputStream(); _output_ = socket.getOutputStream(); _reader = new CRLFLineReader(new InputStreamReader(_input_, __DEFAULT_ENCODING)); __writer = new BufferedWriter(new OutputStreamWriter(_output_, __DEFAULT_ENCODING)); if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { throw new SSLHandshakeException("Hostname doesn't match certificate"); } } /** * Controls which particular cipher suites are enabled for use on this connection. Called before server negotiation. * * @param cipherSuites The cipher suites. */ public void setEnabledCipherSuites(final String[] cipherSuites) { suites = cipherSuites.clone(); } /** * Controls which particular protocol versions are enabled for use on this connection. I perform setting before a server negotiation. * * @param protocolVersions The protocol versions. */ public void setEnabledProtocols(final String[] protocolVersions) { protocols = protocolVersions.clone(); } /** * Automatic endpoint identification checking using the HTTPS algorithm is supported on Java 1.7+. The default behavior is for this to be disabled. * * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+. * @since 3.4 */ public void setEndpointCheckingEnabled(final boolean enable) { tlsEndpointChecking = enable; } /** * Override the default {@link HostnameVerifier} to use. * * @param newHostnameVerifier The HostnameVerifier implementation to set or null to disable. * @since 3.4 */ public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier) { hostnameVerifier = newHostnameVerifier; } /** * Set a {@link KeyManager} to use. * * @param newKeyManager The KeyManager implementation to set. * @see org.apache.commons.net.util.KeyManagerUtils */ public void setKeyManager(final KeyManager newKeyManager) { keyManager = newKeyManager; } /** * Override the default {@link TrustManager} to use. * * @param newTrustManager The TrustManager implementation to set. * @see org.apache.commons.net.util.TrustManagerUtils */ public void setTrustManager(final TrustManager newTrustManager) { trustManager = newTrustManager; } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/imap/package-info.java000066400000000000000000000015701434047722200320310ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Basic IMAP and IMAPS support classes */ package org.apache.commons.net.imap;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/000077500000000000000000000000001434047722200263205ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/CRLFLineReader.java000066400000000000000000000046251434047722200316530ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import org.apache.commons.net.util.NetConstants; /** * CRLFLineReader implements a readLine() method that requires exactly CRLF to terminate an input line. This is required for IMAP, which allows bare CR and LF. * * @since 3.0 */ public final class CRLFLineReader extends BufferedReader { private static final char LF = '\n'; private static final char CR = '\r'; /** * Creates a CRLFLineReader that wraps an existing Reader input source. * * @param reader The Reader input source. */ public CRLFLineReader(final Reader reader) { super(reader); } /** * Read a line of text. A line is considered to be terminated by carriage return followed immediately by a linefeed. This contrasts with BufferedReader * which also allows other combinations. * * @since 3.0 */ @Override public String readLine() throws IOException { final StringBuilder sb = new StringBuilder(); int intch; boolean prevWasCR = false; synchronized (lock) { // make thread-safe (hopefully!) while ((intch = read()) != NetConstants.EOS) { if (prevWasCR && intch == LF) { return sb.substring(0, sb.length() - 1); } prevWasCR = intch == CR; sb.append((char) intch); } } final String string = sb.toString(); if (string.isEmpty()) { // immediate EOF return null; } return string; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/CopyStreamAdapter.java000066400000000000000000000104541434047722200325560ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.util.EventListener; import org.apache.commons.net.util.ListenerList; /** * The CopyStreamAdapter will relay CopyStreamEvents to a list of listeners when either of its bytesTransferred() methods are called. Its purpose is to * facilitate the notification of the progress of a copy operation performed by one of the static copyStream() methods in org.apache.commons.io.Util to multiple * listeners. The static copyStream() methods invoke the bytesTransfered(long, int) of a CopyStreamListener for performance reasons and also because multiple * listeners cannot be registered given that the methods are static. * * * @see CopyStreamEvent * @see CopyStreamListener * @see Util */ public class CopyStreamAdapter implements CopyStreamListener { private final ListenerList internalListeners; /** * Creates a new copyStreamAdapter. */ public CopyStreamAdapter() { internalListeners = new ListenerList(); } /** * Registers a CopyStreamListener to receive CopyStreamEvents. Although this method is not declared to be synchronized, it is implemented in a thread safe * manner. * * @param listener The CopyStreamlistener to register. */ public void addCopyStreamListener(final CopyStreamListener listener) { internalListeners.addListener(listener); } /** * This method is invoked by a CopyStreamEvent source after copying a block of bytes from a stream. The CopyStreamEvent will contain the total number of * bytes transferred so far and the number of bytes transferred in the last write. The CopyStreamAdapater will relay the event to all of its registered * listeners, listing itself as the source of the event. * * @param event The CopyStreamEvent fired by the copying of a block of bytes. */ @Override public void bytesTransferred(final CopyStreamEvent event) { for (final EventListener listener : internalListeners) { ((CopyStreamListener) listener).bytesTransferred(event); } } /** * This method is not part of the JavaBeans model and is used by the static methods in the org.apache.commons.io.Util class for efficiency. It is invoked * after a block of bytes to inform the listener of the transfer. The CopyStreamAdapater will create a CopyStreamEvent from the arguments and relay the * event to all of its registered listeners, listing itself as the source of the event. * * @param totalBytesTransferred The total number of bytes transferred so far by the copy operation. * @param bytesTransferred The number of bytes copied by the most recent write. * @param streamSize The number of bytes in the stream being copied. This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if the size is * unknown. */ @Override public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { for (final EventListener listener : internalListeners) { ((CopyStreamListener) listener).bytesTransferred(totalBytesTransferred, bytesTransferred, streamSize); } } /** * Unregisters a CopyStreamListener. Although this method is not synchronized, it is implemented in a thread safe manner. * * @param listener The CopyStreamlistener to unregister. */ public void removeCopyStreamListener(final CopyStreamListener listener) { internalListeners.removeListener(listener); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/CopyStreamEvent.java000066400000000000000000000066371434047722200322670ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.util.EventObject; /** * A CopyStreamEvent is triggered after every write performed by a stream copying operation. The event stores the number of bytes transferred by the write * triggering the event as well as the total number of bytes transferred so far by the copy operation. * * * @see CopyStreamListener * @see CopyStreamAdapter * @see Util */ public class CopyStreamEvent extends EventObject { private static final long serialVersionUID = -964927635655051867L; /** * Constant used to indicate the stream size is unknown. */ public static final long UNKNOWN_STREAM_SIZE = -1; private final int bytesTransferred; private final long totalBytesTransferred; private final long streamSize; /** * Creates a new CopyStreamEvent instance. * * @param source The source of the event. * @param totalBytesTransferred The total number of bytes transferred so far during a copy operation. * @param bytesTransferred The number of bytes transferred during the write that triggered the CopyStreamEvent. * @param streamSize The number of bytes in the stream being copied. This may be set to UNKNOWN_STREAM_SIZE if the size is unknown. */ public CopyStreamEvent(final Object source, final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { super(source); this.bytesTransferred = bytesTransferred; this.totalBytesTransferred = totalBytesTransferred; this.streamSize = streamSize; } /** * Returns the number of bytes transferred by the write that triggered the event. * * @return The number of bytes transferred by the write that triggered the vent. */ public int getBytesTransferred() { return bytesTransferred; } /** * Returns the size of the stream being copied. This may be set to UNKNOWN_STREAM_SIZE if the size is unknown. * * @return The size of the stream being copied. */ public long getStreamSize() { return streamSize; } /** * Returns the total number of bytes transferred so far by the copy operation. * * @return The total number of bytes transferred so far by the copy operation. */ public long getTotalBytesTransferred() { return totalBytesTransferred; } /** * @since 3.0 */ @Override public String toString() { return getClass().getName() + "[source=" + source + ", total=" + totalBytesTransferred + ", bytes=" + bytesTransferred + ", size=" + streamSize + "]"; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/CopyStreamException.java000066400000000000000000000050461434047722200331350ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.IOException; /** * The CopyStreamException class is thrown by the org.apache.commons.io.Util copyStream() methods. It stores the number of bytes confirmed to have been * transferred before an I/O error as well as the IOException responsible for the failure of a copy operation. * * @see Util */ public class CopyStreamException extends IOException { private static final long serialVersionUID = -2602899129433221532L; private final long totalBytesTransferred; /** * Creates a new CopyStreamException instance. * * @param message A message describing the error. * @param bytesTransferred The total number of bytes transferred before an exception was thrown in a copy operation. * @param exception The IOException thrown during a copy operation. */ public CopyStreamException(final String message, final long bytesTransferred, final IOException exception) { super(message); initCause(exception); // merge this into super() call once we need 1.6+ totalBytesTransferred = bytesTransferred; } /** * Returns the IOException responsible for the failure of a copy operation. * * @return The IOException responsible for the failure of a copy operation. */ public IOException getIOException() { return (IOException) getCause(); // cast is OK because it was initialized with an IOException } /** * Returns the total number of bytes confirmed to have been transferred by a failed copy operation. * * @return The total number of bytes confirmed to have been transferred by a failed copy operation. */ public long getTotalBytesTransferred() { return totalBytesTransferred; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/CopyStreamListener.java000066400000000000000000000055701434047722200327660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.util.EventListener; /** * The CopyStreamListener class can accept CopyStreamEvents to keep track of the progress of a stream copying operation. However, it is currently not used that * way within NetComponents for performance reasons. Rather the bytesTransferred(long, int) method is called directly rather than passing an event to * bytesTransferred(CopyStreamEvent), saving the creation of a CopyStreamEvent instance. Also, the only place where CopyStreamListener is currently used within * NetComponents is in the static methods of the uninstantiable org.apache.commons.io.Util class, which would preclude the use of addCopyStreamListener and * removeCopyStreamListener methods. However, future additions may use the JavaBean event model, which is why the hooks have been included from the beginning. * * * @see CopyStreamEvent * @see CopyStreamAdapter * @see Util */ public interface CopyStreamListener extends EventListener { /** * This method is invoked by a CopyStreamEvent source after copying a block of bytes from a stream. The CopyStreamEvent will contain the total number of * bytes transferred so far and the number of bytes transferred in the last write. * * @param event The CopyStreamEvent fired by the copying of a block of bytes. */ void bytesTransferred(CopyStreamEvent event); /** * This method is not part of the JavaBeans model and is used by the static methods in the org.apache.commons.io.Util class for efficiency. It is invoked * after a block of bytes to inform the listener of the transfer. * * @param totalBytesTransferred The total number of bytes transferred so far by the copy operation. * @param bytesTransferred The number of bytes copied by the most recent write. * @param streamSize The number of bytes in the stream being copied. This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if the size is * unknown. */ void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize); } DotTerminatedMessageReader.java000066400000000000000000000215571434047722200343110ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import org.apache.commons.net.util.NetConstants; /** * DotTerminatedMessageReader is a class used to read messages from a server that are terminated by a single dot followed by a <CR><LF> sequence and * with double dots appearing at the begining of lines which do not signal end of message yet start with a dot. Various Internet protocols such as NNTP and POP3 * produce messages of this type. *

* This class handles stripping of the duplicate period at the beginning of lines starting with a period, and ensures you cannot read past the end of the * message. *

* Note: versions since 3.0 extend BufferedReader rather than Reader, and no longer change the CRLF into the local EOL. Also only DOT CR LF acts as EOF. */ public final class DotTerminatedMessageReader extends BufferedReader { private static final char LF = '\n'; private static final char CR = '\r'; private static final int DOT = '.'; private boolean atBeginning; private boolean eof; private boolean seenCR; // was last character CR? /** * Creates a DotTerminatedMessageReader that wraps an existing Reader input source. * * @param reader The Reader input source containing the message. */ public DotTerminatedMessageReader(final Reader reader) { super(reader); // Assumes input is at start of message atBeginning = true; eof = false; } /** * Closes the message for reading. This doesn't actually close the underlying stream. The underlying stream may still be used for communicating with the * server and therefore is not closed. *

* If the end of the message has not yet been reached, this method will read the remainder of the message until it reaches the end, so that the underlying * stream may continue to be used properly for communicating with the server. If you do not fully read a message, you MUST close it, otherwise your program * will likely hang or behave improperly. * * @throws IOException If an error occurs while reading the underlying stream. */ @Override public void close() throws IOException { synchronized (lock) { if (!eof) { while (read() != -1) { // read to EOF } } eof = true; atBeginning = false; } } /** * Reads and returns the next character in the message. If the end of the message has been reached, returns -1. Note that a call to this method may result * in multiple reads from the underlying input stream to decode the message properly (removing doubled dots and so on). All of this is transparent to the * programmer and is only mentioned for completeness. * * @return The next character in the message. Returns -1 if the end of the message has been reached. * @throws IOException If an error occurs while reading the underlying stream. */ @Override public int read() throws IOException { synchronized (lock) { if (eof) { return NetConstants.EOS; // Don't allow read past EOF } int chint = super.read(); if (chint == NetConstants.EOS) { // True EOF eof = true; return NetConstants.EOS; } if (atBeginning) { atBeginning = false; if (chint == DOT) { // Have DOT mark(2); // need to check for CR LF or DOT chint = super.read(); switch (chint) { case NetConstants.EOS: // new Throwable("Trailing DOT").printStackTrace(); eof = true; return DOT; // return the trailing DOT case DOT: // no need to reset as we want to lose the first DOT return chint; // i.e. DOT case CR: chint = super.read(); if (chint == NetConstants.EOS) { // Still only DOT CR - should not happen // new Throwable("Trailing DOT CR").printStackTrace(); reset(); // So CR is picked up next time return DOT; // return the trailing DOT } if (chint == LF) { // DOT CR LF atBeginning = true; eof = true; // Do we need to clear the mark somehow? return NetConstants.EOS; } break; default: break; } // Should not happen - lone DOT at beginning // new Throwable("Lone DOT followed by "+(char)chint).printStackTrace(); reset(); return DOT; } // have DOT } // atBeginning // Handle CRLF in normal flow if (seenCR) { seenCR = false; if (chint == LF) { atBeginning = true; } } if (chint == CR) { seenCR = true; } return chint; } } /** * Reads the next characters from the message into an array and returns the number of characters read. Returns -1 if the end of the message has been * reached. * * @param buffer The character array in which to store the characters. * @return The number of characters read. Returns -1 if the end of the message has been reached. * @throws IOException If an error occurs in reading the underlying stream. */ @Override public int read(final char[] buffer) throws IOException { return read(buffer, 0, buffer.length); } /** * Reads the next characters from the message into an array and returns the number of characters read. Returns -1 if the end of the message has been * reached. The characters are stored in the array starting from the given offset and up to the length specified. * * @param buffer The character array in which to store the characters. * @param offset The offset into the array at which to start storing characters. * @param length The number of characters to read. * @return The number of characters read. Returns -1 if the end of the message has been reached. * @throws IOException If an error occurs in reading the underlying stream. */ @Override public int read(final char[] buffer, int offset, int length) throws IOException { if (length < 1) { return 0; } int ch; synchronized (lock) { if ((ch = read()) == -1) { return NetConstants.EOS; } final int off = offset; do { buffer[offset++] = (char) ch; } while (--length > 0 && (ch = read()) != -1); return offset - off; } } /** * Read a line of text. A line is considered to be terminated by carriage return followed immediately by a linefeed. This contrasts with BufferedReader * which also allows other combinations. * * @since 3.0 */ @Override public String readLine() throws IOException { final StringBuilder sb = new StringBuilder(); int intch; synchronized (lock) { // make thread-safe (hopefully!) while ((intch = read()) != NetConstants.EOS) { if (intch == LF && atBeginning) { return sb.substring(0, sb.length() - 1); } sb.append((char) intch); } } final String string = sb.toString(); if (string.isEmpty()) { // immediate EOF return null; } // Should not happen - EOF without CRLF // new Throwable(string).printStackTrace(); return string; } } DotTerminatedMessageWriter.java000066400000000000000000000143171434047722200343570ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.IOException; import java.io.Writer; /** * DotTerminatedMessageWriter is a class used to write messages to a server that are terminated by a single dot followed by a <CR><LF> sequence and * with double dots appearing at the begining of lines which do not signal end of message yet start with a dot. Various Internet protocols such as NNTP and POP3 * produce messages of this type. *

* This class handles the doubling of line-starting periods, converts single linefeeds to NETASCII newlines, and on closing will send the final message * terminator dot and NETASCII newline sequence. * * */ public final class DotTerminatedMessageWriter extends Writer { private static final int NOTHING_SPECIAL_STATE = 0; private static final int LAST_WAS_CR_STATE = 1; private static final int LAST_WAS_NL_STATE = 2; private int state; private Writer output; /** * Creates a DotTerminatedMessageWriter that wraps an existing Writer output destination. * * @param output The Writer output destination to write the message. */ public DotTerminatedMessageWriter(final Writer output) { super(output); this.output = output; this.state = NOTHING_SPECIAL_STATE; } /** * Flushes the underlying output, writing all buffered output, but doesn't actually close the underlying stream. The underlying stream may still be used for * communicating with the server and therefore is not closed. * * @throws IOException If an error occurs while writing to the underlying output or closing the Writer. */ @Override public void close() throws IOException { synchronized (lock) { if (output == null) { return; } if (state == LAST_WAS_CR_STATE) { output.write('\n'); } else if (state != LAST_WAS_NL_STATE) { output.write("\r\n"); } output.write(".\r\n"); output.flush(); output = null; } } /** * Flushes the underlying output, writing all buffered output. * * @throws IOException If an error occurs while writing to the underlying output. */ @Override public void flush() throws IOException { synchronized (lock) { output.flush(); } } /** * Writes a character array to the output. * * @param buffer The character array to write. * @throws IOException If an error occurs while writing to the underlying output. */ @Override public void write(final char[] buffer) throws IOException { write(buffer, 0, buffer.length); } /** * Writes a number of characters from a character array to the output starting from a given offset. * * @param buffer The character array to write. * @param offset The offset into the array at which to start copying data. * @param length The number of characters to write. * @throws IOException If an error occurs while writing to the underlying output. */ @Override public void write(final char[] buffer, int offset, int length) throws IOException { synchronized (lock) { while (length-- > 0) { write(buffer[offset++]); } } } /** * Writes a character to the output. Note that a call to this method may result in multiple writes to the underling Writer in order to convert naked * linefeeds to NETASCII line separators and to double line-leading periods. This is transparent to the programmer and is only mentioned for completeness. * * @param ch The character to write. * @throws IOException If an error occurs while writing to the underlying output. */ @Override public void write(final int ch) throws IOException { synchronized (lock) { switch (ch) { case '\r': state = LAST_WAS_CR_STATE; output.write('\r'); return; case '\n': if (state != LAST_WAS_CR_STATE) { output.write('\r'); } output.write('\n'); state = LAST_WAS_NL_STATE; return; case '.': // Double the dot at the beginning of a line if (state == LAST_WAS_NL_STATE) { output.write('.'); } //$FALL-THROUGH$ default: state = NOTHING_SPECIAL_STATE; output.write(ch); } } } /** * Writes a String to the output. * * @param string The String to write. * @throws IOException If an error occurs while writing to the underlying output. */ @Override public void write(final String string) throws IOException { write(string.toCharArray()); } /** * Writes part of a String to the output starting from a given offset. * * @param string The String to write. * @param offset The offset into the String at which to start copying data. * @param length The number of characters to write. * @throws IOException If an error occurs while writing to the underlying output. */ @Override public void write(final String string, final int offset, final int length) throws IOException { write(string.toCharArray(), offset, length); } } FromNetASCIIInputStream.java000066400000000000000000000145021434047722200334250ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.net.util.NetConstants; /** * This class wraps an input stream, replacing all occurrences of <CR><LF> (carriage return followed by a linefeed), which is the NETASCII standard * for representing a newline, with the local line separator representation. You would use this class to implement ASCII file transfers requiring conversion * from NETASCII. * * */ public final class FromNetASCIIInputStream extends PushbackInputStream { static final boolean _noConversionRequired; static final String _lineSeparator; static final byte[] _lineSeparatorBytes; static { _lineSeparator = System.lineSeparator(); _noConversionRequired = _lineSeparator.equals("\r\n"); _lineSeparatorBytes = _lineSeparator.getBytes(StandardCharsets.US_ASCII); } /** * Returns true if the NetASCII line separator differs from the system line separator, false if they are the same. This method is useful to determine * whether or not you need to instantiate a FromNetASCIIInputStream object. * * @return True if the NETASCII line separator differs from the local system line separator, false if they are the same. */ public static boolean isConversionRequired() { return !_noConversionRequired; } private int length; /** * Creates a FromNetASCIIInputStream instance that wraps an existing InputStream. * * @param input the stream to wrap */ public FromNetASCIIInputStream(final InputStream input) { super(input, _lineSeparatorBytes.length + 1); } // PushbackInputStream in JDK 1.1.3 returns the wrong thing // TODO - can we delete this override now? /** * Returns the number of bytes that can be read without blocking EXCEPT when newline conversions have to be made somewhere within the available block of * bytes. In other words, you really should not rely on the value returned by this method if you are trying to avoid blocking. */ @Override public int available() throws IOException { if (in == null) { throw new IOException("Stream closed"); } return buf.length - pos + in.available(); } /** * Reads and returns the next byte in the stream. If the end of the message has been reached, returns -1. Note that a call to this method may result in * multiple reads from the underlying input stream in order to convert NETASCII line separators to the local line separator format. This is transparent to * the programmer and is only mentioned for completeness. * * @return The next character in the stream. Returns -1 if the end of the stream has been reached. * @throws IOException If an error occurs while reading the underlying stream. */ @Override public int read() throws IOException { if (_noConversionRequired) { return super.read(); } return readInt(); } /** * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the stream has been reached. * * @param buffer The byte array in which to store the data. * @return The number of bytes read. Returns -1 if the end of the message has been reached. * @throws IOException If an error occurs in reading the underlying stream. */ @Override public int read(final byte buffer[]) throws IOException { return read(buffer, 0, buffer.length); } /** * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the message has been reached. * The characters are stored in the array starting from the given offset and up to the length specified. * * @param buffer The byte array in which to store the data. * @param offset The offset into the array at which to start storing data. * @param length The number of bytes to read. * @return The number of bytes read. Returns -1 if the end of the stream has been reached. * @throws IOException If an error occurs while reading the underlying stream. */ @Override public int read(final byte buffer[], int offset, final int length) throws IOException { if (_noConversionRequired) { return super.read(buffer, offset, length); } if (length < 1) { return 0; } int ch; final int off; ch = available(); this.length = Math.min(length, ch); // If nothing is available, block to read only one character if (this.length < 1) { this.length = 1; } if ((ch = readInt()) == -1) { return NetConstants.EOS; } off = offset; do { buffer[offset++] = (byte) ch; } while (--this.length > 0 && (ch = readInt()) != -1); return offset - off; } private int readInt() throws IOException { int ch; ch = super.read(); if (ch == '\r') { ch = super.read(); if (ch != '\n') { if (ch != -1) { unread(ch); } return '\r'; } unread(_lineSeparatorBytes); ch = super.read(); // This is a kluge for read(byte[], ...) to read the right amount --length; } return ch; } } FromNetASCIIOutputStream.java000066400000000000000000000114021434047722200336220ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /** * This class wraps an output stream, replacing all occurrences of <CR><LF> (carriage return followed by a linefeed), which is the NETASCII standard * for representing a newline, with the local line separator representation. You would use this class to implement ASCII file transfers requiring conversion * from NETASCII. *

* Because of the translation process, a call to flush() will not flush the last byte written if that byte was a carriage return. A call to * {@link #close close() }, however, will flush the carriage return. * * */ public final class FromNetASCIIOutputStream extends FilterOutputStream { private boolean lastWasCR; /** * Creates a FromNetASCIIOutputStream instance that wraps an existing OutputStream. * * @param output The OutputStream to wrap. */ public FromNetASCIIOutputStream(final OutputStream output) { super(output); lastWasCR = false; } /** * Closes the stream, writing all pending data. * * @throws IOException If an error occurs while closing the stream. */ @Override public synchronized void close() throws IOException { if (FromNetASCIIInputStream._noConversionRequired) { super.close(); return; } if (lastWasCR) { out.write('\r'); } super.close(); } /** * Writes a byte array to the stream. * * @param buffer The byte array to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public synchronized void write(final byte buffer[]) throws IOException { write(buffer, 0, buffer.length); } /** * Writes a number of bytes from a byte array to the stream starting from a given offset. * * @param buffer The byte array to write. * @param offset The offset into the array at which to start copying data. * @param length The number of bytes to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public synchronized void write(final byte buffer[], int offset, int length) throws IOException { if (FromNetASCIIInputStream._noConversionRequired) { // FilterOutputStream method is very slow. // super.write(buffer, offset, length); out.write(buffer, offset, length); return; } while (length-- > 0) { writeInt(buffer[offset++]); } } /** * Writes a byte to the stream. Note that a call to this method might not actually write a byte to the underlying stream until a subsequent character is * written, from which it can be determined if a NETASCII line separator was encountered. This is transparent to the programmer and is only mentioned for * completeness. * * @param ch The byte to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public synchronized void write(final int ch) throws IOException { if (FromNetASCIIInputStream._noConversionRequired) { out.write(ch); return; } writeInt(ch); } private void writeInt(final int ch) throws IOException { switch (ch) { case '\r': lastWasCR = true; // Don't write anything. We need to see if next one is linefeed break; case '\n': if (lastWasCR) { out.write(FromNetASCIIInputStream._lineSeparatorBytes); lastWasCR = false; break; } out.write('\n'); break; default: if (lastWasCR) { out.write('\r'); lastWasCR = false; } out.write(ch); break; } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/SocketInputStream.java000066400000000000000000000041771434047722200326200ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; /** * This class wraps an input stream, storing a reference to its originating socket. When the stream is closed, it will also close the socket immediately * afterward. This class is useful for situations where you are dealing with a stream originating from a socket, but do not have a reference to the socket, and * want to make sure it closes when the stream closes. * * @see SocketOutputStream */ public class SocketInputStream extends FilterInputStream { private final Socket socket; /** * Creates a SocketInputStream instance wrapping an input stream and storing a reference to a socket that should be closed on closing the stream. * * @param socket The socket to close on closing the stream. * @param stream The input stream to wrap. */ public SocketInputStream(final Socket socket, final InputStream stream) { super(stream); this.socket = socket; } /** * Closes the stream and immediately afterward closes the referenced socket. * * @throws IOException If there is an error in closing the stream or socket. */ @Override public void close() throws IOException { super.close(); socket.close(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/SocketOutputStream.java000066400000000000000000000054651434047722200330220ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; /** * This class wraps an output stream, storing a reference to its originating socket. When the stream is closed, it will also close the socket immediately * afterward. This class is useful for situations where you are dealing with a stream originating from a socket, but do not have a reference to the socket, and * want to make sure it closes when the stream closes. * * * @see SocketInputStream */ public class SocketOutputStream extends FilterOutputStream { private final Socket socket; /** * Creates a SocketOutputStream instance wrapping an output stream and storing a reference to a socket that should be closed on closing the stream. * * @param socket The socket to close on closing the stream. * @param stream The input stream to wrap. */ public SocketOutputStream(final Socket socket, final OutputStream stream) { super(stream); this.socket = socket; } /** * Closes the stream and immediately afterward closes the referenced socket. * * @throws IOException If there is an error in closing the stream or socket. */ @Override public void close() throws IOException { super.close(); socket.close(); } /** * Writes a number of bytes from a byte array to the stream starting from a given offset. This method bypasses the equivalent method in FilterOutputStream * because the FilterOutputStream implementation is very inefficient. * * @param buffer The byte array to write. * @param offset The offset into the array at which to start copying data. * @param length The number of bytes to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public void write(final byte buffer[], final int offset, final int length) throws IOException { out.write(buffer, offset, length); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/ToNetASCIIInputStream.java000066400000000000000000000117121434047722200331630ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.net.util.NetConstants; /** * This class wraps an input stream, replacing all singly occurring <LF> (linefeed) characters with <CR><LF> (carriage return followed by * linefeed), which is the NETASCII standard for representing a newline. You would use this class to implement ASCII file transfers requiring conversion to * NETASCII. * * */ public final class ToNetASCIIInputStream extends FilterInputStream { private static final int NOTHING_SPECIAL = 0; private static final int LAST_WAS_CR = 1; private static final int LAST_WAS_NL = 2; private int status; /** * Creates a ToNetASCIIInputStream instance that wraps an existing InputStream. * * @param input The InputStream to wrap. */ public ToNetASCIIInputStream(final InputStream input) { super(input); status = NOTHING_SPECIAL; } @Override public int available() throws IOException { final int result; result = in.available(); if (status == LAST_WAS_NL) { return result + 1; } return result; } /** Returns false. Mark is not supported. */ @Override public boolean markSupported() { return false; } /** * Reads and returns the next byte in the stream. If the end of the message has been reached, returns -1. * * @return The next character in the stream. Returns -1 if the end of the stream has been reached. * @throws IOException If an error occurs while reading the underlying stream. */ @Override public int read() throws IOException { final int ch; if (status == LAST_WAS_NL) { status = NOTHING_SPECIAL; return '\n'; } ch = in.read(); switch (ch) { case '\r': status = LAST_WAS_CR; return '\r'; case '\n': if (status != LAST_WAS_CR) { status = LAST_WAS_NL; return '\r'; } //$FALL-THROUGH$ default: status = NOTHING_SPECIAL; return ch; } // statement not reached // return ch; } /** * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the stream has been reached. * * @param buffer The byte array in which to store the data. * @return The number of bytes read. Returns -1 if the end of the message has been reached. * @throws IOException If an error occurs in reading the underlying stream. */ @Override public int read(final byte[] buffer) throws IOException { return read(buffer, 0, buffer.length); } /** * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the message has been reached. * The characters are stored in the array starting from the given offset and up to the length specified. * * @param buffer The byte array in which to store the data. * @param offset The offset into the array at which to start storing data. * @param length The number of bytes to read. * @return The number of bytes read. Returns -1 if the end of the stream has been reached. * @throws IOException If an error occurs while reading the underlying stream. */ @Override public int read(final byte[] buffer, int offset, int length) throws IOException { int ch; final int off; if (length < 1) { return 0; } ch = available(); if (length > ch) { length = ch; } // If nothing is available, block to read only one character if (length < 1) { length = 1; } if ((ch = read()) == NetConstants.EOS) { return NetConstants.EOS; } off = offset; do { buffer[offset++] = (byte) ch; } while (--length > 0 && (ch = read()) != NetConstants.EOS); return offset - off; } } ToNetASCIIOutputStream.java000066400000000000000000000065261434047722200333140ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /** * This class wraps an output stream, replacing all singly occurring <LF> (linefeed) characters with <CR><LF> (carriage return followed by * linefeed), which is the NETASCII standard for representing a newline. You would use this class to implement ASCII file transfers requiring conversion to * NETASCII. * * */ public final class ToNetASCIIOutputStream extends FilterOutputStream { private boolean lastWasCR; /** * Creates a ToNetASCIIOutputStream instance that wraps an existing OutputStream. * * @param output The OutputStream to wrap. */ public ToNetASCIIOutputStream(final OutputStream output) { super(output); lastWasCR = false; } /** * Writes a byte array to the stream. * * @param buffer The byte array to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public synchronized void write(final byte buffer[]) throws IOException { write(buffer, 0, buffer.length); } /** * Writes a number of bytes from a byte array to the stream starting from a given offset. * * @param buffer The byte array to write. * @param offset The offset into the array at which to start copying data. * @param length The number of bytes to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public synchronized void write(final byte buffer[], int offset, int length) throws IOException { while (length-- > 0) { write(buffer[offset++]); } } /** * Writes a byte to the stream. Note that a call to this method may result in multiple writes to the underlying input stream in order to convert naked * newlines to NETASCII line separators. This is transparent to the programmer and is only mentioned for completeness. * * @param ch The byte to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public synchronized void write(final int ch) throws IOException { switch (ch) { case '\r': lastWasCR = true; out.write('\r'); return; case '\n': if (!lastWasCR) { out.write('\r'); } //$FALL-THROUGH$ default: lastWasCR = false; out.write(ch); } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/Util.java000066400000000000000000000367371434047722200301200ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.net.Socket; import org.apache.commons.net.util.NetConstants; /** * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful. * * * @see CopyStreamException * @see CopyStreamListener * @see CopyStreamAdapter */ public final class Util { /** * The default buffer size ({@value}) used by {@link #copyStream copyStream } and {@link #copyReader copyReader} and by the copyReader/copyStream methods if * a zero or negative buffer size is supplied. */ public static final int DEFAULT_COPY_BUFFER_SIZE = 1024; /** * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks. * * @param closeable the object to close, may be {@code null} * @since 3.0 */ public static void closeQuietly(final Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (final IOException e) { // Ignored } } } /** * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks. * * @param socket the socket to close, may be {@code null} * @since 3.0 */ public static void closeQuietly(final Socket socket) { if (socket != null) { try { socket.close(); } catch (final IOException e) { // Ignored } } } /** * Same as copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); * * @param source where to copy from * @param dest where to copy to * @return number of bytes copied * @throws CopyStreamException on error */ public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException { return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); } /** * Copies the contents of a Reader to a Writer using a copy buffer of a given size. The contents of the Reader are read until its end is reached, but * neither the source nor the destination are closed. You must do this yourself outside of the method call. The number of characters read/written is * returned. * * @param source The source Reader. * @param dest The destination writer. * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. * @return The number of characters read/written in the copy operation. * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and * getIOException() methods. */ public static long copyReader(final Reader source, final Writer dest, final int bufferSize) throws CopyStreamException { return copyReader(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); } /** * Copies the contents of a Reader to a Writer using a copy buffer of a given size and notifies the provided CopyStreamListener of the progress of the copy * operation by calling its bytesTransferred(long, int) method after each write to the destination. If you wish to notify more than one listener you should * use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. *

* The contents of the Reader are read until its end is reached, but neither the source nor the destination are closed. You must do this yourself outside of * the method call. The number of characters read/written is returned. * * @param source The source Reader. * @param dest The destination writer. * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. * @param streamSize The number of characters in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently * used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. * @return The number of characters read/written in the copy operation. * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and * getIOException() methods. */ public static long copyReader(final Reader source, final Writer dest, final int bufferSize, final long streamSize, final CopyStreamListener listener) throws CopyStreamException { int numChars; long total = 0; final char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; try { while ((numChars = source.read(buffer)) != NetConstants.EOS) { // Technically, some read(char[]) methods may return 0 and we cannot // accept that as an indication of EOF. if (numChars == 0) { final int singleChar = source.read(); if (singleChar < 0) { break; } dest.write(singleChar); dest.flush(); ++total; if (listener != null) { listener.bytesTransferred(total, 1, streamSize); } continue; } dest.write(buffer, 0, numChars); dest.flush(); total += numChars; if (listener != null) { listener.bytesTransferred(total, numChars, streamSize); } } } catch (final IOException e) { throw new CopyStreamException("IOException caught while copying.", total, e); } return total; } /** * Same as copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); * * @param source where to copy from * @param dest where to copy to * @return number of bytes copied * @throws CopyStreamException on error */ public static long copyStream(final InputStream source, final OutputStream dest) throws CopyStreamException { return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); } /** * Copies the contents of an InputStream to an OutputStream using a copy buffer of a given size. The contents of the InputStream are read until the end of * the stream is reached, but neither the source nor the destination are closed. You must do this yourself outside of the method call. The number of bytes * read/written is returned. * * @param source The source InputStream. * @param dest The destination OutputStream. * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. * @return The number of bytes read/written in the copy operation. * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and * getIOException() methods. */ public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize) throws CopyStreamException { return copyStream(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); } /** * Copies the contents of an InputStream to an OutputStream using a copy buffer of a given size and notifies the provided CopyStreamListener of the progress * of the copy operation by calling its bytesTransferred(long, int) method after each write to the destination. If you wish to notify more than one listener * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. *

* The contents of the InputStream are read until the end of the stream is reached, but neither the source nor the destination are closed. You must do this * yourself outside of the method call. The number of bytes read/written is returned. * * @param source The source InputStream. * @param dest The destination OutputStream. * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used * (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. * @return number of bytes read/written * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and * getIOException() methods. */ public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize, final CopyStreamListener listener) throws CopyStreamException { return copyStream(source, dest, bufferSize, streamSize, listener, true); } /** * Copies the contents of an InputStream to an OutputStream using a copy buffer of a given size and notifies the provided CopyStreamListener of the progress * of the copy operation by calling its bytesTransferred(long, int) method after each write to the destination. If you wish to notify more than one listener * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. *

* The contents of the InputStream are read until the end of the stream is reached, but neither the source nor the destination are closed. You must do this * yourself outside of the method call. The number of bytes read/written is returned. * * @param source The source InputStream. * @param dest The destination OutputStream. * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used * (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. * @param flush Whether to flush the output stream after every write. This is necessary for interactive sessions that rely on buffered streams. If you * don't flush, the data will stay in the stream buffer. * @return number of bytes read/written * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and * getIOException() methods. */ public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize, final CopyStreamListener listener, final boolean flush) throws CopyStreamException { int numBytes; long total = 0; final byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; try { while ((numBytes = source.read(buffer)) != NetConstants.EOS) { // Technically, some read(byte[]) methods may return 0 and we cannot // accept that as an indication of EOF. if (numBytes == 0) { final int singleByte = source.read(); if (singleByte < 0) { break; } dest.write(singleByte); if (flush) { dest.flush(); } ++total; if (listener != null) { listener.bytesTransferred(total, 1, streamSize); } continue; } dest.write(buffer, 0, numBytes); if (flush) { dest.flush(); } total += numBytes; if (listener != null) { listener.bytesTransferred(total, numBytes, streamSize); } } } catch (final IOException e) { throw new CopyStreamException("IOException caught while copying.", total, e); } return total; } // Cannot be instantiated private Util() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/io/package-info.java000066400000000000000000000015611434047722200315120ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Utility classes for IO support. */ package org.apache.commons.net.io;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/000077500000000000000000000000001434047722200266705ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/Article.java000066400000000000000000000201311434047722200311130ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import org.apache.commons.net.util.NetConstants; /** * This is a class that contains the basic state needed for message retrieval and threading. With thanks to Jamie Zawinski (jwz@jwz.org) */ public class Article implements Threadable { /** * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out. * * @param article the root of the article 'tree' * @since 3.4 */ public static void printThread(final Article article) { printThread(article, 0, System.out); } /** * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out. * * @param article the root of the article 'tree' * @param depth the current tree depth */ public static void printThread(final Article article, final int depth) { printThread(article, depth, System.out); } /** * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out. * * @param article the root of the article 'tree' * @param depth the current tree depth * @param ps the PrintStream to use * @since 3.4 */ public static void printThread(final Article article, final int depth, final PrintStream ps) { for (int i = 0; i < depth; ++i) { ps.print("==>"); } ps.println(article.getSubject() + "\t" + article.getFrom() + "\t" + article.getArticleId()); if (article.kid != null) { printThread(article.kid, depth + 1); } if (article.next != null) { printThread(article.next, depth); } } /** * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out. * * @param article the root of the article 'tree' * @param ps the PrintStream to use * @since 3.4 */ public static void printThread(final Article article, final PrintStream ps) { printThread(article, 0, ps); } private long articleNumber; private String subject; private String date; private String articleId; private String simplifiedSubject; private String from; private ArrayList references; private boolean isReply; public Article kid, next; public Article() { articleNumber = -1; // isDummy } @Deprecated public void addHeaderField(final String name, final String val) { } /** * Adds a message-id to the list of messages that this message references (i.e. replies to) * * @param msgId the message id to add */ public void addReference(final String msgId) { if (msgId == null || msgId.isEmpty()) { return; } if (references == null) { references = new ArrayList<>(); } isReply = true; Collections.addAll(references, msgId.split(" ")); } private void flushSubjectCache() { simplifiedSubject = null; } public String getArticleId() { return articleId; } @Deprecated public int getArticleNumber() { return (int) articleNumber; } public long getArticleNumberLong() { return articleNumber; } public String getDate() { return date; } public String getFrom() { return from; } /** * Returns the MessageId references as an array of Strings * * @return an array of message-ids */ public String[] getReferences() { if (references == null) { return NetConstants.EMPTY_STRING_ARRAY; } return references.toArray(NetConstants.EMPTY_STRING_ARRAY); } public String getSubject() { return subject; } @Override public boolean isDummy() { return (articleNumber == -1); } @Override public Threadable makeDummy() { return new Article(); } @Override public String messageThreadId() { return articleId; } @Override public String[] messageThreadReferences() { return getReferences(); } public void setArticleId(final String string) { articleId = string; } @Deprecated public void setArticleNumber(final int a) { articleNumber = a; } public void setArticleNumber(final long l) { articleNumber = l; } @Override public void setChild(final Threadable child) { this.kid = (Article) child; flushSubjectCache(); } public void setDate(final String string) { date = string; } public void setFrom(final String string) { from = string; } @Override public void setNext(final Threadable next) { this.next = (Article) next; flushSubjectCache(); } public void setSubject(final String string) { subject = string; } @Override public String simplifiedSubject() { if (simplifiedSubject == null) { simplifySubject(); } return simplifiedSubject; } // DEPRECATED METHODS - for API compatibility only - DO NOT USE /** * Attempts to parse the subject line for some typical reply signatures, and strip them out * */ private void simplifySubject() { int start = 0; final String subject = getSubject(); final int len = subject.length(); boolean done = false; while (!done) { done = true; // skip whitespace // "Re: " breaks this while (start < len && subject.charAt(start) == ' ') { start++; } if (start < (len - 2) && (subject.charAt(start) == 'r' || subject.charAt(start) == 'R') && (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) { if (subject.charAt(start + 2) == ':') { start += 3; // Skip "Re:" done = false; } else if (start < (len - 2) && (subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) { int i = start + 3; while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9') { i++; } if (i < (len - 1) && (subject.charAt(i) == ']' || subject.charAt(i) == ')') && subject.charAt(i + 1) == ':') { start = i + 2; done = false; } } } if ("(no subject)".equals(simplifiedSubject)) { simplifiedSubject = ""; } int end = len; while (end > start && subject.charAt(end - 1) < ' ') { end--; } if (start == 0 && end == len) { simplifiedSubject = subject; } else { simplifiedSubject = subject.substring(start, end); } } } @Override public boolean subjectIsReply() { return isReply; } @Override public String toString() { // Useful for Eclipse debugging return articleNumber + " " + articleId + " " + subject; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/ArticleInfo.java000066400000000000000000000021621434047722200317330ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.nntp; /** * Class contains details about an article. Create an instance of the class and pass it to the appropriate NNTP method. The values will be populated on return. * */ public class ArticleInfo { public String articleId; public long articleNumber; public ArticleInfo() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/ArticleIterator.java000066400000000000000000000035541434047722200326370ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.nntp; import java.util.Iterator; /** * Class which wraps an {@code Iterable} of raw article information to generate an {@code Iterable

} of the parsed information. * * @since 3.0 */ class ArticleIterator implements Iterator
, Iterable
{ private final Iterator stringIterator; public ArticleIterator(final Iterable iterableString) { stringIterator = iterableString.iterator(); } @Override public boolean hasNext() { return stringIterator.hasNext(); } @Override public Iterator
iterator() { return this; } /** * Get the next Article * * @return the next {@link Article}, never {@code null}, if unparseable then isDummy() will be true, and the subject will contain the raw info. */ @Override public Article next() { final String line = stringIterator.next(); return NNTPClient.parseArticleEntry(line); } @Override public void remove() { stringIterator.remove(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/ArticlePointer.java000066400000000000000000000030571434047722200324640ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * This class is a structure used to return article number and unique id information extracted from an NNTP server reply. You will normally want this * information when issuing a STAT command, implemented by {@link NNTPClient#selectArticle selectArticle}. * * @see NNTPClient * * @deprecated 3.0 use {@link ArticleInfo} instead */ @Deprecated public final class ArticlePointer { /** The number of the referenced article. */ public int articleNumber; /** * The unique id of the referenced article, including the enclosing < and > symbols which are technically not part of the identifier, but are required * by all NNTP commands taking an article id as an argument. */ public String articleId; } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/NNTP.java000066400000000000000000001247241434047722200303240ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.commons.net.MalformedServerReplyException; import org.apache.commons.net.ProtocolCommandSupport; import org.apache.commons.net.SocketClient; import org.apache.commons.net.io.CRLFLineReader; /** * The NNTP class is not meant to be used by itself and is provided only so that you may easily implement your own NNTP client if you so desire. If you have no * need to perform your own implementation, you should use {@link org.apache.commons.net.nntp.NNTPClient}. The NNTP class is made public to provide access to * various NNTP constants and to make it easier for adventurous programmers (or those with special needs) to interact with the NNTP protocol and implement their * own clients. A set of methods with names corresponding to the NNTP command names are provided to facilitate this interaction. *

* You should keep in mind that the NNTP server may choose to prematurely close a connection if the client has been idle for longer than a given time period or * if the server is being shutdown by the operator or some other reason. The NNTP class will detect a premature NNTP server connection closing when it receives * a {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED } response to a command. When that occurs, the NNTP class * method encountering that reply will throw an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} . NNTPConectionClosedException is * a subclass of IOException and therefore need not be caught separately, but if you are going to catch it separately, its catch block must * appear before the more general IOException catch block. When you encounter an * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} , you must disconnect the connection with {@link #disconnect disconnect() } to properly * clean up the system resources used by NNTP. Before disconnecting, you may check the last reply code and text with {@link #getReplyCode getReplyCode } and * {@link #getReplyString getReplyString }. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. * * @see NNTPClient * @see NNTPConnectionClosedException * @see org.apache.commons.net.MalformedServerReplyException */ public class NNTP extends SocketClient { /** The default NNTP port. Its value is 119 according to RFC 977. */ public static final int DEFAULT_PORT = 119; // We have to ensure that the protocol communication is in ASCII // but we use ISO-8859-1 just in case 8-bit characters cross // the wire. private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1; boolean _isAllowedToPost; private int replyCode; private String replyString; /** * Wraps {@link SocketClient#_input_} to communicate with server. Initialized by {@link #_connectAction_}. All server reads should be done through this * variable. */ protected BufferedReader _reader_; /** * Wraps {@link SocketClient#_output_} to communicate with server. Initialized by {@link #_connectAction_}. All server reads should be done through this * variable. */ protected BufferedWriter _writer_; /** * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and te firing of ProtocolCommandEvents. */ protected ProtocolCommandSupport _commandSupport_; /** * The default NNTP constructor. Sets the default port to DEFAULT_PORT and initializes internal data structures for saving NNTP reply * information. */ public NNTP() { setDefaultPort(DEFAULT_PORT); replyString = null; _reader_ = null; _writer_ = null; _isAllowedToPost = false; _commandSupport_ = new ProtocolCommandSupport(this); } /** * Initiates control connections and gets initial reply, determining if the client is allowed to post to the server. Initializes {@link #_reader_} and * {@link #_writer_} to wrap {@link SocketClient#_input_} and {@link SocketClient#_output_}. */ @Override protected void _connectAction_() throws IOException { super._connectAction_(); _reader_ = new CRLFLineReader(new InputStreamReader(_input_, DEFAULT_ENCODING)); _writer_ = new BufferedWriter(new OutputStreamWriter(_output_, DEFAULT_ENCODING)); getReply(); _isAllowedToPost = replyCode == NNTPReply.SERVER_READY_POSTING_ALLOWED; } /** * A convenience method to send the NNTP ARTICLE command to the server, receive the initial reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int article() throws IOException { return sendCommand(NNTPCommand.ARTICLE); } /** * @param a article number * @return number * @throws IOException on error * @deprecated - for API compatibility only - DO NOT USE */ @Deprecated public int article(final int a) throws IOException { return article((long) a); } /** * A convenience method to send the NNTP ARTICLE command to the server, receive the initial reply, and return the reply code. *

* * @param articleNumber The number of the article to request from the currently selected newsgroup. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int article(final long articleNumber) throws IOException { return sendCommand(NNTPCommand.ARTICLE, Long.toString(articleNumber)); } /** * A convenience method to send the NNTP ARTICLE command to the server, receive the initial reply, and return the reply code. *

* * @param messageId The message identifier of the requested article, including the encapsulating < and > characters. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int article(final String messageId) throws IOException { return sendCommand(NNTPCommand.ARTICLE, messageId); } /** * A convenience method to send the AUTHINFO PASS command to the server, receive the reply, and return the reply code. If this step is required, it should * immediately follow the AUTHINFO USER command (See RFC 2980) *

* * @param password a valid password. * @return The reply code received from the server. The server should return a 281 or 502 for this command. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int authinfoPass(final String password) throws IOException { final String passParameter = "PASS " + password; return sendCommand(NNTPCommand.AUTHINFO, passParameter); } /** * A convenience method to send the AUTHINFO USER command to the server, receive the reply, and return the reply code. (See RFC 2980) *

* * @param username A valid username. * @return The reply code received from the server. The server should return a 381 or 281 for this command. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int authinfoUser(final String username) throws IOException { final String userParameter = "USER " + username; return sendCommand(NNTPCommand.AUTHINFO, userParameter); } /** * A convenience method to send the NNTP BODY command to the server, receive the initial reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int body() throws IOException { return sendCommand(NNTPCommand.BODY); } /** * @param a article number * @return number * @throws IOException on error * @deprecated - for API compatibility only - DO NOT USE */ @Deprecated public int body(final int a) throws IOException { return body((long) a); } /** * A convenience method to send the NNTP BODY command to the server, receive the initial reply, and return the reply code. *

* * @param articleNumber The number of the article to request from the currently selected newsgroup. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int body(final long articleNumber) throws IOException { return sendCommand(NNTPCommand.BODY, Long.toString(articleNumber)); } /** * A convenience method to send the NNTP BODY command to the server, receive the initial reply, and return the reply code. *

* * @param messageId The message identifier of the requested article, including the encapsulating < and > characters. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int body(final String messageId) throws IOException { return sendCommand(NNTPCommand.BODY, messageId); } /** * Closes the connection to the NNTP server and sets to null some internal data so that the memory may be reclaimed by the garbage collector. The reply text * and code information from the last command is voided so that the memory it used may be reclaimed. *

* * @throws IOException If an error occurs while disconnecting. */ @Override public void disconnect() throws IOException { super.disconnect(); _reader_ = null; _writer_ = null; replyString = null; _isAllowedToPost = false; } /** * Provide command support to super-class */ @Override protected ProtocolCommandSupport getCommandSupport() { return _commandSupport_; } /** * Fetches a reply from the NNTP server and returns the integer reply code. After calling this method, the actual reply text can be accessed from * {@link #getReplyString getReplyString }. Only use this method if you are implementing your own NNTP client or if you need to fetch a secondary response * from the NNTP server. *

* * @return The integer value of the reply code of the fetched NNTP reply. in response to the command. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while receiving the server reply. */ public int getReply() throws IOException { replyString = _reader_.readLine(); if (replyString == null) { throw new NNTPConnectionClosedException("Connection closed without indication."); } // In case we run into an anomaly we don't want fatal index exceptions // to be thrown. if (replyString.length() < 3) { throw new MalformedServerReplyException("Truncated server reply: " + replyString); } try { replyCode = Integer.parseInt(replyString.substring(0, 3)); } catch (final NumberFormatException e) { throw new MalformedServerReplyException("Could not parse response code.\nServer Reply: " + replyString); } fireReplyReceived(replyCode, replyString + SocketClient.NETASCII_EOL); if (replyCode == NNTPReply.SERVICE_DISCONTINUED) { throw new NNTPConnectionClosedException("NNTP response 400 received. Server closed connection."); } return replyCode; } /** * Returns the integer value of the reply code of the last NNTP reply. You will usually only use this method after you connect to the NNTP server to check * that the connection was successful since connect is of type void. *

* * @return The integer value of the reply code of the last NNTP reply. */ public int getReplyCode() { return replyCode; } /** * Returns the entire text of the last NNTP server response exactly as it was received, not including the end of line marker. *

* * @return The entire text from the last NNTP response as a String. */ public String getReplyString() { return replyString; } /** * A convenience method to send the NNTP GROUP command to the server, receive the reply, and return the reply code. *

* * @param newsgroup The name of the newsgroup to select. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int group(final String newsgroup) throws IOException { return sendCommand(NNTPCommand.GROUP, newsgroup); } /** * A convenience method to send the NNTP HEAD command to the server, receive the initial reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int head() throws IOException { return sendCommand(NNTPCommand.HEAD); } /** * @param a article number * @return number * @throws IOException on error * @deprecated - for API compatibility only - DO NOT USE */ @Deprecated public int head(final int a) throws IOException { return head((long) a); } /** * A convenience method to send the NNTP HEAD command to the server, receive the initial reply, and return the reply code. *

* * @param articleNumber The number of the article to request from the currently selected newsgroup. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int head(final long articleNumber) throws IOException { return sendCommand(NNTPCommand.HEAD, Long.toString(articleNumber)); } /** * A convenience method to send the NNTP HEAD command to the server, receive the initial reply, and return the reply code. *

* * @param messageId The message identifier of the requested article, including the encapsulating < and > characters. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int head(final String messageId) throws IOException { return sendCommand(NNTPCommand.HEAD, messageId); } /** * A convenience method to send the NNTP HELP command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int help() throws IOException { return sendCommand(NNTPCommand.HELP); } /** * A convenience method to send the NNTP IHAVE command to the server, receive the reply, and return the reply code. *

* * @param messageId The article identifier, including the encapsulating < and > characters. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int ihave(final String messageId) throws IOException { return sendCommand(NNTPCommand.IHAVE, messageId); } /** * Indicates whether or not the client is allowed to post articles to the server it is currently connected to. *

* * @return True if the client can post articles to the server, false otherwise. */ public boolean isAllowedToPost() { return _isAllowedToPost; } /** * A convenience method to send the NNTP LAST command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int last() throws IOException { return sendCommand(NNTPCommand.LAST); } /** * A convenience method to send the NNTP LIST command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int list() throws IOException { return sendCommand(NNTPCommand.LIST); } /** * A convenience wrapper for the extended LIST command that takes an argument, allowing us to selectively list multiple groups. *

* * @param wildmat A wildmat (pseudo-regex) pattern. See RFC 2980 for details. * @return the reply code received from the server. * @throws IOException if the command fails */ public int listActive(final String wildmat) throws IOException { final StringBuilder command = new StringBuilder("ACTIVE "); command.append(wildmat); return sendCommand(NNTPCommand.LIST, command.toString()); } /** * A convenience method to send the "NEWGROUPS" command to the server, receive the reply, and return the reply code. *

* * @param date The date after which to check for new groups. Date format is YYMMDD * @param time The time after which to check for new groups. Time format is HHMMSS using a 24-hour clock. * @param GMT True if the time is in GMT, false if local server time. * @param distributions Comma-separated distribution list to check for new groups. Set to null if no distributions. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int newgroups(final String date, final String time, final boolean GMT, final String distributions) throws IOException { final StringBuilder buffer = new StringBuilder(); buffer.append(date); buffer.append(' '); buffer.append(time); if (GMT) { buffer.append(' '); buffer.append("GMT"); } if (distributions != null) { buffer.append(" <"); buffer.append(distributions); buffer.append('>'); } return sendCommand(NNTPCommand.NEWGROUPS, buffer.toString()); } /** * A convenience method to send the "NEWNEWS" command to the server, receive the reply, and return the reply code. *

* * @param newsgroups A comma-separated list of newsgroups to check for new news. * @param date The date after which to check for new news. Date format is YYMMDD * @param time The time after which to check for new news. Time format is HHMMSS using a 24-hour clock. * @param GMT True if the time is in GMT, false if local server time. * @param distributions Comma-separated distribution list to check for new news. Set to null if no distributions. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int newnews(final String newsgroups, final String date, final String time, final boolean GMT, final String distributions) throws IOException { final StringBuilder buffer = new StringBuilder(); buffer.append(newsgroups); buffer.append(' '); buffer.append(date); buffer.append(' '); buffer.append(time); if (GMT) { buffer.append(' '); buffer.append("GMT"); } if (distributions != null) { buffer.append(" <"); buffer.append(distributions); buffer.append('>'); } return sendCommand(NNTPCommand.NEWNEWS, buffer.toString()); } /** * A convenience method to send the NNTP NEXT command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int next() throws IOException { return sendCommand(NNTPCommand.NEXT); } /** * A convenience method to send the NNTP POST command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int post() throws IOException { return sendCommand(NNTPCommand.POST); } /** * A convenience method to send the NNTP QUIT command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int quit() throws IOException { return sendCommand(NNTPCommand.QUIT); } /** * Sends an NNTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString }. *

* * @param command The NNTPCommand constant corresponding to the NNTP command to send. * @return The integer value of the NNTP reply code returned by the server in response to the command. in response to the command. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final int command) throws IOException { return sendCommand(command, null); } /** * Sends an NNTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString }. *

* * @param command The NNTPCommand constant corresponding to the NNTP command to send. * @param args The arguments to the NNTP command. If this parameter is set to null, then the command is sent with no argument. * @return The integer value of the NNTP reply code returned by the server in response to the command. in response to the command. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final int command, final String args) throws IOException { return sendCommand(NNTPCommand.getCommand(command), args); } /** * Sends an NNTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString }. *

* * @param command The text representation of the NNTP command to send. * @return The integer value of the NNTP reply code returned by the server in response to the command. in response to the command. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final String command) throws IOException { return sendCommand(command, null); } /** * Sends an NNTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString }. *

* * @param command The text representation of the NNTP command to send. * @param args The arguments to the NNTP command. If this parameter is set to null, then the command is sent with no argument. * @return The integer value of the NNTP reply code returned by the server in response to the command. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final String command, final String args) throws IOException { final StringBuilder __commandBuffer = new StringBuilder(); __commandBuffer.append(command); if (args != null) { __commandBuffer.append(' '); __commandBuffer.append(args); } __commandBuffer.append(SocketClient.NETASCII_EOL); final String message; _writer_.write(message = __commandBuffer.toString()); _writer_.flush(); fireCommandSent(command, message); return getReply(); } /** * A convenience method to send the NNTP STAT command to the server, receive the initial reply, and return the reply code. *

* * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stat() throws IOException { return sendCommand(NNTPCommand.STAT); } // DEPRECATED METHODS - for API compatibility only - DO NOT USE /** * @param a article number * @return number * @throws IOException on error * @deprecated - for API compatibility only - DO NOT USE */ @Deprecated public int stat(final int a) throws IOException { return stat((long) a); } /** * A convenience method to send the NNTP STAT command to the server, receive the initial reply, and return the reply code. *

* * @param articleNumber The number of the article to request from the currently selected newsgroup. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stat(final long articleNumber) throws IOException { return sendCommand(NNTPCommand.STAT, Long.toString(articleNumber)); } /** * A convenience method to send the NNTP STAT command to the server, receive the initial reply, and return the reply code. *

* * @param messageId The message identifier of the requested article, including the encapsulating < and > characters. * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int stat(final String messageId) throws IOException { return sendCommand(NNTPCommand.STAT, messageId); } /** * A convenience method to send the NNTP XHDR command to the server, receive the reply, and return the reply code. *

* * @param header a String naming a header line (e.g., "subject"). See RFC-1036 for a list of valid header lines. * @param selectedArticles a String representation of the range of article headers required. This may be an article number, or a range of article numbers in * the form "XXXX-YYYY", where XXXX and YYYY are valid article numbers in the current group. It also may be of the form "XXX-", * meaning "return XXX and all following articles" In this revision, the last format is not possible (yet). * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int xhdr(final String header, final String selectedArticles) throws IOException { final StringBuilder command = new StringBuilder(header); command.append(" "); command.append(selectedArticles); return sendCommand(NNTPCommand.XHDR, command.toString()); } /** * A convenience method to send the NNTP XOVER command to the server, receive the reply, and return the reply code. *

* * @param selectedArticles a String representation of the range of article headers required. This may be an article number, or a range of article numbers in * the form "XXXX-YYYY", where XXXX and YYYY are valid article numbers in the current group. It also may be of the form "XXX-", * meaning "return XXX and all following articles" In this revision, the last format is not possible (yet). * @return The reply code received from the server. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int xover(final String selectedArticles) throws IOException { return sendCommand(NNTPCommand.XOVER, selectedArticles); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/NNTPClient.java000066400000000000000000002250351434047722200314600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Vector; import org.apache.commons.net.MalformedServerReplyException; import org.apache.commons.net.io.DotTerminatedMessageReader; import org.apache.commons.net.io.DotTerminatedMessageWriter; import org.apache.commons.net.io.Util; import org.apache.commons.net.util.NetConstants; /** * NNTPClient encapsulates all the functionality necessary to post and retrieve articles from an NNTP server. As with all classes derived from * {@link org.apache.commons.net.SocketClient}, you must first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before * doing anything, and finally {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() } after you're completely finished interacting with the server. * Remember that the {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()} method is defined in {@link org.apache.commons.net.nntp.NNTP}. *

* You should keep in mind that the NNTP server may choose to prematurely close a connection if the client has been idle for longer than a given time period or * if the server is being shutdown by the operator or some other reason. The NNTP class will detect a premature NNTP server connection closing when it receives * a {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED } response to a command. When that occurs, the NNTP class * method encountering that reply will throw an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} . NNTPConectionClosedException is * a subclass of IOException and therefore need not be caught separately, but if you are going to catch it separately, its catch block must * appear before the more general IOException catch block. When you encounter an * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException} , you must disconnect the connection with * {@link org.apache.commons.net.nntp.NNTP#disconnect disconnect() } to properly clean up the system resources used by NNTP. Before disconnecting, you may check * the last reply code and text with {@link org.apache.commons.net.nntp.NNTP#getReplyCode getReplyCode } and * {@link org.apache.commons.net.nntp.NNTP#getReplyString getReplyString }. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. * * @see NNTP * @see NNTPConnectionClosedException * @see org.apache.commons.net.MalformedServerReplyException */ public class NNTPClient extends NNTP { private static final NewsgroupInfo[] EMPTY_NEWSGROUP_INFO_ARRAY = {}; /** * Parse a response line from {@link #retrieveArticleInfo(long, long)}. * * @param line a response line * @return the parsed {@link Article}, if unparseable then isDummy() will be true, and the subject will contain the raw info. * @since 3.0 */ static Article parseArticleEntry(final String line) { // Extract the article information // Mandatory format (from NNTP RFC 2980) is : // articleNumber\tSubject\tAuthor\tDate\tID\tReference(s)\tByte Count\tLine Count final Article article = new Article(); article.setSubject(line); // in case parsing fails final String parts[] = line.split("\t"); if (parts.length > 6) { int i = 0; try { article.setArticleNumber(Long.parseLong(parts[i++])); article.setSubject(parts[i++]); article.setFrom(parts[i++]); article.setDate(parts[i++]); article.setArticleId(parts[i++]); article.addReference(parts[i++]); } catch (final NumberFormatException e) { // ignored, already handled } } return article; } /* * 211 n f l s group selected (n = estimated number of articles in group, f = first article number in the group, l = last article number in the group, s = * name of the group.) */ private static void parseGroupReply(final String reply, final NewsgroupInfo info) throws MalformedServerReplyException { final String tokens[] = reply.split(" "); if (tokens.length >= 5) { int i = 1; // Skip numeric response value try { // Get estimated article count info.setArticleCount(Long.parseLong(tokens[i++])); // Get first article number info.setFirstArticle(Long.parseLong(tokens[i++])); // Get last article number info.setLastArticle(Long.parseLong(tokens[i++])); // Get newsgroup name info.setNewsgroup(tokens[i++]); info.setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION); return; } catch (final NumberFormatException e) { // drop through to report error } } throw new MalformedServerReplyException("Could not parse newsgroup info.\nServer reply: " + reply); } // Format: group last first p static NewsgroupInfo parseNewsgroupListEntry(final String entry) { final String tokens[] = entry.split(" "); if (tokens.length < 4) { return null; } final NewsgroupInfo result = new NewsgroupInfo(); int i = 0; result.setNewsgroup(tokens[i++]); try { final long lastNum = Long.parseLong(tokens[i++]); final long firstNum = Long.parseLong(tokens[i++]); result.setFirstArticle(firstNum); result.setLastArticle(lastNum); if ((firstNum == 0) && (lastNum == 0)) { result.setArticleCount(0); } else { result.setArticleCount(lastNum - firstNum + 1); } } catch (final NumberFormatException e) { return null; } switch (tokens[i++].charAt(0)) { case 'y': case 'Y': result.setPostingPermission(NewsgroupInfo.PERMITTED_POSTING_PERMISSION); break; case 'n': case 'N': result.setPostingPermission(NewsgroupInfo.PROHIBITED_POSTING_PERMISSION); break; case 'm': case 'M': result.setPostingPermission(NewsgroupInfo.MODERATED_POSTING_PERMISSION); break; default: result.setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION); break; } return result; } @SuppressWarnings("deprecation") private void ai2ap(final ArticleInfo ai, final ArticlePointer ap) { if (ap != null) { // ai cannot be null ap.articleId = ai.articleId; ap.articleNumber = (int) ai.articleNumber; } } private ArticleInfo ap2ai(@SuppressWarnings("deprecation") final ArticlePointer ap) { if (ap == null) { return null; } final ArticleInfo ai = new ArticleInfo(); return ai; } /** * Log into a news server by sending the AUTHINFO USER/AUTHINFO PASS command sequence. This is usually sent in response to a 480 reply code from the NNTP * server. *

* * @param username a valid username * @param password the corresponding password * @return True for successful login, false for a failure * @throws IOException on error */ public boolean authenticate(final String username, final String password) throws IOException { int replyCode = authinfoUser(username); if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED) { replyCode = authinfoPass(password); if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED) { this._isAllowedToPost = true; return true; } } return false; } /** * There are a few NNTPClient methods that do not complete the entire sequence of NNTP commands to complete a transaction. These commands require some * action by the programmer after the reception of a positive preliminary command. After the programmer's code completes its actions, it must call this * method to receive the completion reply from the server and verify the success of the entire transaction. *

* For example * *

     * writer = client.postArticle();
     * if (writer == null) // failure
     *     return false;
     * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
     * header.addNewsgroup("alt.test");
     * writer.write(header.toString());
     * writer.write("This is just a test");
     * writer.close();
     * if (!client.completePendingCommand()) // failure
     *     return false;
     * 
*

* * @return True if successfully completed, false if not. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean completePendingCommand() throws IOException { return NNTPReply.isPositiveCompletion(getReply()); } public Writer forwardArticle(final String articleId) throws IOException { if (!NNTPReply.isPositiveIntermediate(ihave(articleId))) { return null; } return new DotTerminatedMessageWriter(_writer_); } /** * Return article headers for all articles between lowArticleNumber and highArticleNumber, inclusively, using the XOVER command. *

* * @param lowArticleNumber low * @param highArticleNumber high * @return an Iterable of Articles * @throws IOException if the command failed * @since 3.0 */ public Iterable

iterateArticleInfo(final long lowArticleNumber, final long highArticleNumber) throws IOException { final BufferedReader info = retrieveArticleInfo(lowArticleNumber, highArticleNumber); if (info == null) { throw new IOException("XOVER command failed: " + getReplyString()); } // N.B. info is already DotTerminated, so don't rewrap return new ArticleIterator(new ReplyIterator(info, false)); } /** * List all new articles added to the NNTP server since a particular date subject to the conditions of the specified query. If no new new news is found, no * entries will be returned. This uses the "NEWNEWS" command. You must add at least one newsgroup to the query, else the command will fail. Each String * which is returned is a unique message identifier including the enclosing < and >. *

* * @param query The query restricting how to search for new news. You must add at least one newsgroup to the query. * @return An iterator of String instances containing the unique message identifiers for each new article added to the NNTP server. If no new news is found, * no strings will be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.0 */ public Iterable iterateNewNews(final NewGroupsOrNewsQuery query) throws IOException { if (NNTPReply.isPositiveCompletion(newnews(query.getNewsgroups(), query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { return new ReplyIterator(_reader_); } throw new IOException("NEWNEWS command failed: " + getReplyString()); } /** * List all new newsgroups added to the NNTP server since a particular date subject to the conditions of the specified query. If no new newsgroups were * added, no entries will be returned. This uses the "NEWGROUPS" command. *

* * @param query The query restricting how to search for new newsgroups. * @return An iterable of Strings containing the raw information for each new newsgroup added to the NNTP server. If no newsgroups were added, no entries * will be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.0 */ public Iterable iterateNewNewsgroupListing(final NewGroupsOrNewsQuery query) throws IOException { if (NNTPReply.isPositiveCompletion(newgroups(query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { return new ReplyIterator(_reader_); } throw new IOException("NEWGROUPS command failed: " + getReplyString()); } /** * List all new newsgroups added to the NNTP server since a particular date subject to the conditions of the specified query. If no new newsgroups were * added, no entries will be returned. This uses the "NEWGROUPS" command. *

* * @param query The query restricting how to search for new newsgroups. * @return An iterable of NewsgroupInfo instances containing the information for each new newsgroup added to the NNTP server. If no newsgroups were added, * no entries will be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.0 */ public Iterable iterateNewNewsgroups(final NewGroupsOrNewsQuery query) throws IOException { return new NewsgroupIterator(iterateNewNewsgroupListing(query)); } /** * List all newsgroups served by the NNTP server. If no newsgroups are served, no entries will be returned. The method uses the "LIST" command. *

* * @return An iterable of NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server. If no newsgroups are served, no * entries will be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.0 */ public Iterable iterateNewsgroupListing() throws IOException { if (NNTPReply.isPositiveCompletion(list())) { return new ReplyIterator(_reader_); } throw new IOException("LIST command failed: " + getReplyString()); } /** * List the newsgroups that match a given pattern. Uses the "LIST ACTIVE" command. *

* * @param wildmat a pseudo-regex pattern (cf. RFC 2980) * @return An iterable of Strings containing the raw information for each newsgroup served by the NNTP server corresponding to the supplied pattern. If no * such newsgroups are served, no entries will be returned. * @throws IOException on error * @since 3.0 */ public Iterable iterateNewsgroupListing(final String wildmat) throws IOException { if (NNTPReply.isPositiveCompletion(listActive(wildmat))) { return new ReplyIterator(_reader_); } throw new IOException("LIST ACTIVE " + wildmat + " command failed: " + getReplyString()); } /** * List all newsgroups served by the NNTP server. If no newsgroups are served, no entries will be returned. The method uses the "LIST" command. *

* * @return An iterable of Strings containing the raw information for each newsgroup served by the NNTP server. If no newsgroups are served, no entries will * be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @since 3.0 */ public Iterable iterateNewsgroups() throws IOException { return new NewsgroupIterator(iterateNewsgroupListing()); } /** * List the newsgroups that match a given pattern. Uses the "LIST ACTIVE" command. *

* * @param wildmat a pseudo-regex pattern (cf. RFC 2980) * @return An iterable NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server corresponding to the supplied * pattern. If no such newsgroups are served, no entries will be returned. * @throws IOException on error * @since 3.0 */ public Iterable iterateNewsgroups(final String wildmat) throws IOException { return new NewsgroupIterator(iterateNewsgroupListing(wildmat)); } /** * List the command help from the server. *

* * @return The sever help information. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String listHelp() throws IOException { if (!NNTPReply.isInformational(help())) { return null; } try (final StringWriter help = new StringWriter(); final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { Util.copyReader(reader, help); return help.toString(); } } /** * List all new articles added to the NNTP server since a particular date subject to the conditions of the specified query. If no new new news is found, a * zero length array will be returned. If the command fails, null will be returned. You must add at least one newsgroup to the query, else the command will * fail. Each String in the returned array is a unique message identifier including the enclosing < and >. This uses the "NEWNEWS" command. *

* * @param query The query restricting how to search for new news. You must add at least one newsgroup to the query. * @return An array of String instances containing the unique message identifiers for each new article added to the NNTP server. If no new news is found, a * zero length array will be returned. If the command fails, null will be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * * @see #iterateNewNews(NewGroupsOrNewsQuery) */ public String[] listNewNews(final NewGroupsOrNewsQuery query) throws IOException { if (!NNTPReply.isPositiveCompletion(newnews(query.getNewsgroups(), query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { return null; } final Vector list = new Vector<>(); try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { String line; while ((line = reader.readLine()) != null) { list.addElement(line); } } final int size = list.size(); if (size < 1) { return NetConstants.EMPTY_STRING_ARRAY; } final String[] result = new String[size]; list.copyInto(result); return result; } /** * List all new newsgroups added to the NNTP server since a particular date subject to the conditions of the specified query. If no new newsgroups were * added, a zero length array will be returned. If the command fails, null will be returned. This uses the "NEWGROUPS" command. *

* * @param query The query restricting how to search for new newsgroups. * @return An array of NewsgroupInfo instances containing the information for each new newsgroup added to the NNTP server. If no newsgroups were added, a * zero length array will be returned. If the command fails, null will be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @see #iterateNewNewsgroups(NewGroupsOrNewsQuery) * @see #iterateNewNewsgroupListing(NewGroupsOrNewsQuery) */ public NewsgroupInfo[] listNewNewsgroups(final NewGroupsOrNewsQuery query) throws IOException { if (!NNTPReply.isPositiveCompletion(newgroups(query.getDate(), query.getTime(), query.isGMT(), query.getDistributions()))) { return null; } return readNewsgroupListing(); } /** * List all newsgroups served by the NNTP server. If no newsgroups are served, a zero length array will be returned. If the command fails, null will be * returned. The method uses the "LIST" command. *

* * @return An array of NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server. If no newsgroups are served, a zero * length array will be returned. If the command fails, null will be returned. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @see #iterateNewsgroupListing() * @see #iterateNewsgroups() */ public NewsgroupInfo[] listNewsgroups() throws IOException { if (!NNTPReply.isPositiveCompletion(list())) { return null; } return readNewsgroupListing(); } /** * List the newsgroups that match a given pattern. Uses the "LIST ACTIVE" command. *

* * @param wildmat a pseudo-regex pattern (cf. RFC 2980) * @return An array of NewsgroupInfo instances containing the information for each newsgroup served by the NNTP server corresponding to the supplied * pattern. If no such newsgroups are served, a zero length array will be returned. If the command fails, null will be returned. * @throws IOException on error * @see #iterateNewsgroupListing(String) * @see #iterateNewsgroups(String) */ public NewsgroupInfo[] listNewsgroups(final String wildmat) throws IOException { if (!NNTPReply.isPositiveCompletion(listActive(wildmat))) { return null; } return readNewsgroupListing(); } /** * Send a "LIST OVERVIEW.FMT" command to the server. * * @return the contents of the Overview format, of {@code null} if the command failed * @throws IOException on error */ public String[] listOverviewFmt() throws IOException { if (!NNTPReply.isPositiveCompletion(sendCommand("LIST", "OVERVIEW.FMT"))) { return null; } try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { String line; final ArrayList list = new ArrayList<>(); while ((line = reader.readLine()) != null) { list.add(line); } return list.toArray(NetConstants.EMPTY_STRING_ARRAY); } } /** * Logs out of the news server gracefully by sending the QUIT command. However, you must still disconnect from the server before you can open a new * connection. *

* * @return True if successfully completed, false if not. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean logout() throws IOException { return NNTPReply.isPositiveCompletion(quit()); } /** * Parse the reply and store the id and number in the pointer. * * @param reply the reply to parse "22n nnn " * @param pointer the pointer to update * * @throws MalformedServerReplyException if response could not be parsed */ private void parseArticlePointer(final String reply, final ArticleInfo pointer) throws MalformedServerReplyException { final String tokens[] = reply.split(" "); if (tokens.length >= 3) { // OK, we can parset the line int i = 1; // skip reply code try { // Get article number pointer.articleNumber = Long.parseLong(tokens[i++]); // Get article id pointer.articleId = tokens[i++]; return; // done } catch (final NumberFormatException e) { // drop through and raise exception } } throw new MalformedServerReplyException("Could not parse article pointer.\nServer reply: " + reply); } /** * Post an article to the NNTP server. This method returns a DotTerminatedMessageWriter instance to which the article can be written. Null is returned if * the posting attempt fails. You should check {@link NNTP#isAllowedToPost isAllowedToPost() } before trying to post. However, a posting attempt can fail * due to malformed headers. *

* You must not issue any commands to the NNTP server (i.e., call any (other methods) until you finish writing to the returned Writer instance and close it. * The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned Writer actually writes directly to * the NNTP connection. After you close the writer, you can execute new commands. If you do not follow these requirements your program will not work * properly. *

* Different NNTP servers will require different header formats, but you can use the provided {@link org.apache.commons.net.nntp.SimpleNNTPHeader} class to * construct the bare minimum acceptable header for most news readers. To construct more complicated headers you should refer to RFC 822. When the Java Mail * API is finalized, you will be able to use it to compose fully compliant Internet text messages. The DotTerminatedMessageWriter takes care of doubling * line-leading dots and ending the message with a single dot upon closing, so all you have to worry about is writing the header and the message. *

* Upon closing the returned Writer, you need to call {@link #completePendingCommand completePendingCommand() } to finalize the posting and verify its * success or failure from the server reply. *

* * @return A DotTerminatedMessageWriter to which the article (including header) can be written. Returns null if the command fails. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public Writer postArticle() throws IOException { if (!NNTPReply.isPositiveIntermediate(post())) { return null; } return new DotTerminatedMessageWriter(_writer_); } private NewsgroupInfo[] readNewsgroupListing() throws IOException { // Start of with a big vector because we may be reading a very large // amount of groups. final Vector list = new Vector<>(2048); String line; try (final BufferedReader reader = new DotTerminatedMessageReader(_reader_)) { while ((line = reader.readLine()) != null) { final NewsgroupInfo tmp = parseNewsgroupListEntry(line); if (tmp == null) { throw new MalformedServerReplyException(line); } list.addElement(tmp); } } final int size; if ((size = list.size()) < 1) { return EMPTY_NEWSGROUP_INFO_ARRAY; } final NewsgroupInfo[] info = new NewsgroupInfo[size]; list.copyInto(info); return info; } private BufferedReader retrieve(final int command, final long articleNumber, final ArticleInfo pointer) throws IOException { if (!NNTPReply.isPositiveCompletion(sendCommand(command, Long.toString(articleNumber)))) { return null; } if (pointer != null) { parseArticlePointer(getReplyString(), pointer); } return new DotTerminatedMessageReader(_reader_); } private BufferedReader retrieve(final int command, final String articleId, final ArticleInfo pointer) throws IOException { if (articleId != null) { if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId))) { return null; } } else if (!NNTPReply.isPositiveCompletion(sendCommand(command))) { return null; } if (pointer != null) { parseArticlePointer(getReplyString(), pointer); } return new DotTerminatedMessageReader(_reader_); } /** * Same as retrieveArticle((String) null) Note: the return can be cast to a {@link BufferedReader} * * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws IOException if an IO error occurs */ public Reader retrieveArticle() throws IOException { return retrieveArticle((String) null); } /** * @param articleNumber The number of the the article to retrieve * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws IOException on error * @deprecated 3.0 use {@link #retrieveArticle(long)} instead */ @Deprecated public Reader retrieveArticle(final int articleNumber) throws IOException { return retrieveArticle((long) articleNumber); } /** * @param articleNumber The number of the the article to retrieve. * @param pointer A parameter through which to return the article's number and unique id * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws IOException on error * @deprecated 3.0 use {@link #retrieveArticle(long, ArticleInfo)} instead */ @Deprecated public Reader retrieveArticle(final int articleNumber, final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final Reader rdr = retrieveArticle(articleNumber, ai); ai2ap(ai, pointer); return rdr; } /** * Same as retrieveArticle(articleNumber, null) * * @param articleNumber the article number to fetch * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws IOException if an IO error occurs */ public BufferedReader retrieveArticle(final long articleNumber) throws IOException { return retrieveArticle(articleNumber, null); } /** * Retrieves an article from the currently selected newsgroup. The article is referenced by its article number. The article number and identifier contained * in the server reply are returned through an ArticleInfo. The articleId field of the ArticleInfo cannot always be trusted because some NNTP * servers do not correctly follow the RFC 977 reply format. *

* A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. *

* You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. *

* * @param articleNumber The number of the the article to retrieve. * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of * server deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned * article information. * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public BufferedReader retrieveArticle(final long articleNumber, final ArticleInfo pointer) throws IOException { return retrieve(NNTPCommand.ARTICLE, articleNumber, pointer); } /** * Same as retrieveArticle(articleId, (ArticleInfo) null) Note: the return can be cast to a {@link BufferedReader} * * @param articleId the article id to retrieve * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws IOException if an IO error occurs */ public Reader retrieveArticle(final String articleId) throws IOException { return retrieveArticle(articleId, (ArticleInfo) null); } /** * Retrieves an article from the NNTP server. The article is referenced by its unique article identifier (including the enclosing < and >). The * article number and identifier contained in the server reply are returned through an ArticleInfo. The articleId field of the ArticleInfo * cannot always be trusted because some NNTP servers do not correctly follow the RFC 977 reply format. *

* A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. *

* You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. *

* * @param articleId The unique article identifier of the article to retrieve. If this parameter is null, the currently selected article is retrieved. * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article * information. * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public BufferedReader retrieveArticle(final String articleId, final ArticleInfo pointer) throws IOException { return retrieve(NNTPCommand.ARTICLE, articleId, pointer); } /** * @param articleId The unique article identifier of the article to retrieve * @param pointer A parameter through which to return the article's number and unique id * @deprecated 3.0 use {@link #retrieveArticle(String, ArticleInfo)} instead * @return A DotTerminatedMessageReader instance from which the article can be read. null if the article does not exist. * @throws IOException on error */ @Deprecated public Reader retrieveArticle(final String articleId, final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final Reader rdr = retrieveArticle(articleId, ai); ai2ap(ai, pointer); return rdr; } /** * Same as retrieveArticleBody(null) Note: the return can be cast to a {@link BufferedReader} * * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. * @throws IOException if an error occurs */ public Reader retrieveArticleBody() throws IOException { return retrieveArticleBody(null); } /** * @param a tba * @return tba * @throws IOException tba * @deprecated 3.0 use {@link #retrieveArticleBody(long)} instead */ @Deprecated public Reader retrieveArticleBody(final int a) throws IOException { return retrieveArticleBody((long) a); } /** * @param a tba * @param ap tba * @return tba * @throws IOException tba * @deprecated 3.0 use {@link #retrieveArticleBody(long, ArticleInfo)} instead */ @Deprecated public Reader retrieveArticleBody(final int a, final ArticlePointer ap) throws IOException { final ArticleInfo ai = ap2ai(ap); final Reader rdr = retrieveArticleBody(a, ai); ai2ap(ai, ap); return rdr; } /** * Same as retrieveArticleBody(articleNumber, null) * * @param articleNumber the article number * @return the reader * @throws IOException if an error occurs */ public BufferedReader retrieveArticleBody(final long articleNumber) throws IOException { return retrieveArticleBody(articleNumber, null); } /** * Retrieves an article body from the currently selected newsgroup. The article is referenced by its article number. The article number and identifier * contained in the server reply are returned through an ArticleInfo. The articleId field of the ArticleInfo cannot always be trusted because * some NNTP servers do not correctly follow the RFC 977 reply format. *

* A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. *

* You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. *

* * @param articleNumber The number of the the article whose body is being retrieved. * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of * server deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned * article information. * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public BufferedReader retrieveArticleBody(final long articleNumber, final ArticleInfo pointer) throws IOException { return retrieve(NNTPCommand.BODY, articleNumber, pointer); } /** * Same as retrieveArticleBody(articleId, (ArticleInfo) null) Note: the return can be cast to a {@link BufferedReader} * * @param articleId the article id * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. * @throws IOException if an error occurs */ public Reader retrieveArticleBody(final String articleId) throws IOException { return retrieveArticleBody(articleId, (ArticleInfo) null); } /** * Retrieves an article body from the NNTP server. The article is referenced by its unique article identifier (including the enclosing < and >). The * article number and identifier contained in the server reply are returned through an ArticleInfo. The articleId field of the ArticleInfo * cannot always be trusted because some NNTP servers do not correctly follow the RFC 977 reply format. *

* A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. *

* You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. *

* * @param articleId The unique article identifier of the article whose body is being retrieved. If this parameter is null, the body of the currently * selected article is retrieved. * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article * information. * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public BufferedReader retrieveArticleBody(final String articleId, final ArticleInfo pointer) throws IOException { return retrieve(NNTPCommand.BODY, articleId, pointer); } /** * @param articleId The unique article identifier of the article to retrieve * @param pointer A parameter through which to return the article's number and unique id * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. * @throws IOException on error * @deprecated 3.0 use {@link #retrieveArticleBody(String, ArticleInfo)} instead */ @Deprecated public Reader retrieveArticleBody(final String articleId, final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final Reader rdr = retrieveArticleBody(articleId, ai); ai2ap(ai, pointer); return rdr; } /** * Same as retrieveArticleHeader((String) null) Note: the return can be cast to a {@link BufferedReader} * * @return the reader * @throws IOException if an error occurs */ public Reader retrieveArticleHeader() throws IOException { return retrieveArticleHeader((String) null); } /** * @param a tba * @return tba * @throws IOException tba * @deprecated 3.0 use {@link #retrieveArticleHeader(long)} instead */ @Deprecated public Reader retrieveArticleHeader(final int a) throws IOException { return retrieveArticleHeader((long) a); } /** * @param a tba * @param ap tba * @return tba * @throws IOException tba * @deprecated 3.0 use {@link #retrieveArticleHeader(long, ArticleInfo)} instead */ @Deprecated public Reader retrieveArticleHeader(final int a, final ArticlePointer ap) throws IOException { final ArticleInfo ai = ap2ai(ap); final Reader rdr = retrieveArticleHeader(a, ai); ai2ap(ai, ap); return rdr; } /** * Same as retrieveArticleHeader(articleNumber, null) * * @param articleNumber the article number * @return the reader * @throws IOException if an error occurs */ public BufferedReader retrieveArticleHeader(final long articleNumber) throws IOException { return retrieveArticleHeader(articleNumber, null); } /** * Retrieves an article header from the currently selected newsgroup. The article is referenced by its article number. The article number and identifier * contained in the server reply are returned through an ArticleInfo. The articleId field of the ArticleInfo cannot always be trusted because * some NNTP servers do not correctly follow the RFC 977 reply format. *

* A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. *

* You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. *

* * @param articleNumber The number of the the article whose header is being retrieved. * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of * server deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned * article information. * @return A DotTerminatedMessageReader instance from which the article header can be read. null if the article does not exist. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public BufferedReader retrieveArticleHeader(final long articleNumber, final ArticleInfo pointer) throws IOException { return retrieve(NNTPCommand.HEAD, articleNumber, pointer); } /** * Same as retrieveArticleHeader(articleId, (ArticleInfo) null) Note: the return can be cast to a {@link BufferedReader} * * @param articleId the article id to fetch * @return the reader * @throws IOException if an error occurs */ public Reader retrieveArticleHeader(final String articleId) throws IOException { return retrieveArticleHeader(articleId, (ArticleInfo) null); } /** * Retrieves an article header from the NNTP server. The article is referenced by its unique article identifier (including the enclosing < and >). The * article number and identifier contained in the server reply are returned through an ArticleInfo. The articleId field of the ArticleInfo * cannot always be trusted because some NNTP servers do not correctly follow the RFC 977 reply format. *

* A DotTerminatedMessageReader is returned from which the article can be read. If the article does not exist, null is returned. *

* You must not issue any commands to the NNTP server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The NNTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the NNTP connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. *

* * @param articleId The unique article identifier of the article whose header is being retrieved. If this parameter is null, the header of the currently * selected article is retrieved. * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article * information. * @return A DotTerminatedMessageReader instance from which the article header can be read. null if the article does not exist. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public BufferedReader retrieveArticleHeader(final String articleId, final ArticleInfo pointer) throws IOException { return retrieve(NNTPCommand.HEAD, articleId, pointer); } /** * @param articleId The unique article identifier of the article to retrieve * @param pointer A parameter through which to return the article's number and unique id * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. * @throws IOException on error * @deprecated 3.0 use {@link #retrieveArticleHeader(String, ArticleInfo)} instead */ @Deprecated public Reader retrieveArticleHeader(final String articleId, final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final Reader rdr = retrieveArticleHeader(articleId, ai); ai2ap(ai, pointer); return rdr; } /** * @param lowArticleNumber to fetch * @return a DotTerminatedReader if successful, null otherwise * @throws IOException tba * @deprecated 3.0 use {@link #retrieveArticleInfo(long)} instead */ @Deprecated public Reader retrieveArticleInfo(final int lowArticleNumber) throws IOException { return retrieveArticleInfo((long) lowArticleNumber); } /** * @param lowArticleNumber to fetch * @param highArticleNumber to fetch * @return a DotTerminatedReader if successful, null otherwise * @throws IOException on error * @deprecated 3.0 use {@link #retrieveArticleInfo(long, long)} instead */ @Deprecated public Reader retrieveArticleInfo(final int lowArticleNumber, final int highArticleNumber) throws IOException { return retrieveArticleInfo((long) lowArticleNumber, (long) highArticleNumber); } /** * Return article headers for a specified post. *

* * @param articleNumber the article to retrieve headers for * @return a DotTerminatedReader if successful, null otherwise * @throws IOException on error */ public BufferedReader retrieveArticleInfo(final long articleNumber) throws IOException { return retrieveArticleInfo(Long.toString(articleNumber)); } /** * Return article headers for all articles between lowArticleNumber and highArticleNumber, inclusively. Uses the XOVER command. *

* * @param lowArticleNumber low number * @param highArticleNumber high number * @return a DotTerminatedReader if successful, null otherwise * @throws IOException on error */ public BufferedReader retrieveArticleInfo(final long lowArticleNumber, final long highArticleNumber) throws IOException { return retrieveArticleInfo(lowArticleNumber + "-" + highArticleNumber); } /** * Private implementation of XOVER functionality. * * See {@link NNTP#xover} for legal agument formats. Alternatively, read RFC 2980 :-) *

* * @param articleRange * @return Returns a DotTerminatedMessageReader if successful, null otherwise * @throws IOException */ private BufferedReader retrieveArticleInfo(final String articleRange) throws IOException { if (!NNTPReply.isPositiveCompletion(xover(articleRange))) { return null; } return new DotTerminatedMessageReader(_reader_); } /** * @param a tba * @param b tba * @return tba * @throws IOException tba * @deprecated 3.0 use {@link #retrieveHeader(String, long)} instead */ @Deprecated public Reader retrieveHeader(final String a, final int b) throws IOException { return retrieveHeader(a, (long) b); } // DEPRECATED METHODS - for API compatibility only - DO NOT USE // ============================================================ /** * @param header the header * @param lowArticleNumber to fetch * @param highArticleNumber to fetch * @return a DotTerminatedReader if successful, null otherwise * @throws IOException on error * @deprecated 3.0 use {@link #retrieveHeader(String, long, long)} instead */ @Deprecated public Reader retrieveHeader(final String header, final int lowArticleNumber, final int highArticleNumber) throws IOException { return retrieveHeader(header, (long) lowArticleNumber, (long) highArticleNumber); } /** * Return an article header for a specified post. *

* * @param header the header to retrieve * @param articleNumber the article to retrieve the header for * @return a DotTerminatedReader if successful, null otherwise * @throws IOException on error */ public BufferedReader retrieveHeader(final String header, final long articleNumber) throws IOException { return retrieveHeader(header, Long.toString(articleNumber)); } /** * Return an article header for all articles between lowArticleNumber and highArticleNumber, inclusively. *

* * @param header the header * @param lowArticleNumber to fetch * @param highArticleNumber to fetch * @return a DotTerminatedReader if successful, null otherwise * @throws IOException on error */ public BufferedReader retrieveHeader(final String header, final long lowArticleNumber, final long highArticleNumber) throws IOException { return retrieveHeader(header, lowArticleNumber + "-" + highArticleNumber); } /** * Private implementation of XHDR functionality. * * See {@link NNTP#xhdr} for legal agument formats. Alternatively, read RFC 1036. *

* * @param header * @param articleRange * @return Returns a DotTerminatedMessageReader if successful, null otherwise * @throws IOException */ private BufferedReader retrieveHeader(final String header, final String articleRange) throws IOException { if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange))) { return null; } return new DotTerminatedMessageReader(_reader_); } /*** * Same as selectArticle((String) null, articleId) . Useful for retrieving the current article number. * * @param pointer to the article * @return true if OK * @throws IOException on error */ public boolean selectArticle(final ArticleInfo pointer) throws IOException { return selectArticle(null, pointer); } /** * @param pointer A parameter through which to return the article's number and unique id * @return True if successful, false if not. * @throws IOException on error * @deprecated 3.0 use {@link #selectArticle(ArticleInfo)} instead */ @Deprecated public boolean selectArticle(final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final boolean b = selectArticle(ai); ai2ap(ai, pointer); return b; } /** * @param a tba * @return tba * @throws IOException tba * @deprecated 3.0 use {@link #selectArticle(long)} instead */ @Deprecated public boolean selectArticle(final int a) throws IOException { return selectArticle((long) a); } /** * @param a tba * @param ap tba * @return tba * @throws IOException tba * @deprecated 3.0 use {@link #selectArticle(long, ArticleInfo)} instead */ @Deprecated public boolean selectArticle(final int a, final ArticlePointer ap) throws IOException { final ArticleInfo ai = ap2ai(ap); final boolean b = selectArticle(a, ai); ai2ap(ai, ap); return b; } /** * Same as selectArticle(articleNumber, null) * * @param articleNumber the numger * @return true if successful * @throws IOException on error */ public boolean selectArticle(final long articleNumber) throws IOException { return selectArticle(articleNumber, null); } /** * Select an article in the currently selected newsgroup by its number. and return its article number and id through the pointer parameter. This is achieved * through the STAT command. According to RFC 977, this WILL set the current article pointer on the server. Use this command to select an article before * retrieving it, or to obtain an article's unique identifier given its number. *

* * @param articleNumber The number of the article to select from the currently selected newsgroup. * @param pointer A parameter through which to return the article's number and unique id. Although the articleId field cannot always be trusted * because of server deviations from RFC 977 reply formats, we haven't found a server that misformats this information in response to * this particular command. You may set this parameter to null if you do not desire to retrieve the returned article information. * @return True if successful, false if not. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean selectArticle(final long articleNumber, final ArticleInfo pointer) throws IOException { if (!NNTPReply.isPositiveCompletion(stat(articleNumber))) { return false; } if (pointer != null) { parseArticlePointer(getReplyString(), pointer); } return true; } /** * Same as selectArticle(articleId, (ArticleInfo) null) * * @param articleId the article Id * @return true if successful * @throws IOException on error */ public boolean selectArticle(final String articleId) throws IOException { return selectArticle(articleId, (ArticleInfo) null); } /** * Select an article by its unique identifier (including enclosing < and >) and return its article number and id through the pointer parameter. This * is achieved through the STAT command. According to RFC 977, this will NOT set the current article pointer on the server. To do that, you must reference * the article by its number. *

* * @param articleId The unique article identifier of the article that is being selectedd. If this parameter is null, the body of the current article is * selected * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article * information. * @return True if successful, false if not. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean selectArticle(final String articleId, final ArticleInfo pointer) throws IOException { if (articleId != null) { if (!NNTPReply.isPositiveCompletion(stat(articleId))) { return false; } } else if (!NNTPReply.isPositiveCompletion(stat())) { return false; } if (pointer != null) { parseArticlePointer(getReplyString(), pointer); } return true; } /** * @param articleId The unique article identifier of the article to retrieve * @param pointer A parameter through which to return the article's number and unique id * @return A DotTerminatedMessageReader instance from which the article body can be read. null if the article does not exist. * @throws IOException on error * @deprecated 3.0 use {@link #selectArticle(String, ArticleInfo)} instead */ @Deprecated public boolean selectArticle(final String articleId, final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final boolean b = selectArticle(articleId, ai); ai2ap(ai, pointer); return b; } /** * Same as selectNewsgroup(newsgroup, null) * * @param newsgroup the newsgroup name * @return true if newsgroup exist and was selected * @throws IOException if an error occurs */ public boolean selectNewsgroup(final String newsgroup) throws IOException { return selectNewsgroup(newsgroup, null); } /** * Select the specified newsgroup to be the target of for future article retrieval and posting operations. Also return the newsgroup information contained * in the server reply through the info parameter. *

* * @param newsgroup The newsgroup to select. * @param info A parameter through which the newsgroup information of the selected newsgroup contained in the server reply is returned. Set this to * null if you do not desire this information. * @return True if the newsgroup exists and was selected, false otherwise. * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean selectNewsgroup(final String newsgroup, final NewsgroupInfo info) throws IOException { if (!NNTPReply.isPositiveCompletion(group(newsgroup))) { return false; } if (info != null) { parseGroupReply(getReplyString(), info); } return true; } /** * Same as selectNextArticle((ArticleInfo) null) * * @return true if successful * @throws IOException on error */ public boolean selectNextArticle() throws IOException { return selectNextArticle((ArticleInfo) null); } /** * Select the article following the currently selected article in the currently selected newsgroup and return its number and unique id through the pointer * parameter. Because of deviating server implementations, the articleId information cannot be trusted. To obtain the article identifier, issue a * selectArticle(pointer.articleNumber, pointer) immediately afterward. *

* * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article * information. * @return True if successful, false if not (e.g., there is no following article). * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean selectNextArticle(final ArticleInfo pointer) throws IOException { if (!NNTPReply.isPositiveCompletion(next())) { return false; } if (pointer != null) { parseArticlePointer(getReplyString(), pointer); } return true; } /** * @param pointer A parameter through which to return the article's number and unique id * @return True if successful, false if not. * @throws IOException on error * @deprecated 3.0 use {@link #selectNextArticle(ArticleInfo)} instead */ @Deprecated public boolean selectNextArticle(final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final boolean b = selectNextArticle(ai); ai2ap(ai, pointer); return b; } /** * Same as selectPreviousArticle((ArticleInfo) null) * * @return true if successful * @throws IOException on error */ public boolean selectPreviousArticle() throws IOException { return selectPreviousArticle((ArticleInfo) null); } // Helper methods /** * Select the article preceeding the currently selected article in the currently selected newsgroup and return its number and unique id through the pointer * parameter. Because of deviating server implementations, the articleId information cannot be trusted. To obtain the article identifier, issue a * selectArticle(pointer.articleNumber, pointer) immediately afterward. *

* * @param pointer A parameter through which to return the article's number and unique id. The articleId field cannot always be trusted because of server * deviations from RFC 977 reply formats. You may set this parameter to null if you do not desire to retrieve the returned article * information. * @return True if successful, false if not (e.g., there is no previous article). * @throws NNTPConnectionClosedException If the NNTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send NNTP reply code 400. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean selectPreviousArticle(final ArticleInfo pointer) throws IOException { if (!NNTPReply.isPositiveCompletion(last())) { return false; } if (pointer != null) { parseArticlePointer(getReplyString(), pointer); } return true; } /** * @param pointer A parameter through which to return the article's number and unique id * @return True if successful, false if not. * @throws IOException on error * @deprecated 3.0 use {@link #selectPreviousArticle(ArticleInfo)} instead */ @Deprecated public boolean selectPreviousArticle(final ArticlePointer pointer) throws IOException { final ArticleInfo ai = ap2ai(pointer); final boolean b = selectPreviousArticle(ai); ai2ap(ai, pointer); return b; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/NNTPCommand.java000066400000000000000000000045201434047722200316120ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * NNTPCommand stores a set of constants for NNTP command codes. To interpret the meaning of the codes, familiarity with RFC 977 is assumed. */ public final class NNTPCommand { public static final int ARTICLE = 0; public static final int BODY = 1; public static final int GROUP = 2; public static final int HEAD = 3; public static final int HELP = 4; public static final int IHAVE = 5; public static final int LAST = 6; public static final int LIST = 7; public static final int NEWGROUPS = 8; public static final int NEWNEWS = 9; public static final int NEXT = 10; public static final int POST = 11; public static final int QUIT = 12; public static final int SLAVE = 13; public static final int STAT = 14; public static final int AUTHINFO = 15; public static final int XOVER = 16; public static final int XHDR = 17; private static final String[] commands = { "ARTICLE", "BODY", "GROUP", "HEAD", "HELP", "IHAVE", "LAST", "LIST", "NEWGROUPS", "NEWNEWS", "NEXT", "POST", "QUIT", "SLAVE", "STAT", "AUTHINFO", "XOVER", "XHDR" }; /** * Retrieve the NNTP protocol command string corresponding to a specified command code. *

* * @param command The command code. * @return The NNTP protcol command string corresponding to a specified command code. */ public static String getCommand(final int command) { return commands[command]; } // Cannot be instantiated private NNTPCommand() { } } NNTPConnectionClosedException.java000066400000000000000000000035571434047722200352760ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; import java.io.IOException; /** * NNTPConnectionClosedException is used to indicate the premature or unexpected closing of an NNTP connection resulting from a * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED } response (NNTP reply code 400) to a failed NNTP command. * This exception is derived from IOException and therefore may be caught either as an IOException or specifically as an NNTPConnectionClosedException. * * @see NNTP * @see NNTPClient */ public final class NNTPConnectionClosedException extends IOException { private static final long serialVersionUID = 1029785635891040770L; /** Constructs a NNTPConnectionClosedException with no message */ public NNTPConnectionClosedException() { } /** * Constructs a NNTPConnectionClosedException with a specified message. *

* * @param message The message explaining the reason for the exception. */ public NNTPConnectionClosedException(final String message) { super(message); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/NNTPReply.java000066400000000000000000000144531434047722200313350ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * NNTPReply stores a set of constants for NNTP reply codes. To interpret the meaning of the codes, familiarity with RFC 977 is assumed. The mnemonic constant * names are transcriptions from the code descriptions of RFC 977. */ public final class NNTPReply { public static final int HELP_TEXT_FOLLOWS = 100; public static final int DEBUG_OUTPUT = 199; public static final int SERVER_READY_POSTING_ALLOWED = 200; public static final int SERVER_READY_POSTING_NOT_ALLOWED = 201; public static final int SLAVE_STATUS_NOTED = 202; public static final int CLOSING_CONNECTION = 205; public static final int GROUP_SELECTED = 211; public static final int ARTICLE_RETRIEVED_HEAD_AND_BODY_FOLLOW = 220; public static final int ARTICLE_RETRIEVED_HEAD_FOLLOWS = 221; public static final int ARTICLE_RETRIEVED_BODY_FOLLOWS = 222; public static final int ARTICLE_RETRIEVED_REQUEST_TEXT_SEPARATELY = 223; public static final int ARTICLE_LIST_BY_MESSAGE_ID_FOLLOWS = 230; public static final int NEW_NEWSGROUP_LIST_FOLLOWS = 231; public static final int ARTICLE_TRANSFERRED_OK = 235; public static final int ARTICLE_POSTED_OK = 240; public static final int AUTHENTICATION_ACCEPTED = 281; public static final int SEND_ARTICLE_TO_TRANSFER = 335; public static final int SEND_ARTICLE_TO_POST = 340; public static final int MORE_AUTH_INFO_REQUIRED = 381; public static final int SERVICE_DISCONTINUED = 400; public static final int NO_SUCH_NEWSGROUP = 411; public static final int NO_NEWSGROUP_SELECTED = 412; public static final int NO_CURRENT_ARTICLE_SELECTED = 420; public static final int NO_NEXT_ARTICLE = 421; public static final int NO_PREVIOUS_ARTICLE = 422; public static final int NO_SUCH_ARTICLE_NUMBER = 423; public static final int NO_SUCH_ARTICLE_FOUND = 430; public static final int ARTICLE_NOT_WANTED = 435; public static final int TRANSFER_FAILED = 436; public static final int ARTICLE_REJECTED = 437; public static final int POSTING_NOT_ALLOWED = 440; public static final int POSTING_FAILED = 441; /** @since 2.2 - corrected value to 480 */ public static final int AUTHENTICATION_REQUIRED = 480; public static final int AUTHENTICATION_REJECTED = 482; public static final int COMMAND_NOT_RECOGNIZED = 500; public static final int COMMAND_SYNTAX_ERROR = 501; public static final int PERMISSION_DENIED = 502; public static final int PROGRAM_FAULT = 503; // Cannot be instantiated /** * Determine if a reply code is an informational response. All codes beginning with a 1 are positive informational responses. Informational responses are * used to provide human readable information such as help text. *

* * @param reply The reply code to test. * @return True if a reply code is an informational response, false if not. */ public static boolean isInformational(final int reply) { return reply >= 100 && reply < 200; } /** * Determine if a reply code is a negative permanent response. All codes beginning with a 5 are negative permanent responses. The NNTP server will send a * negative permanent response when it does not implement a command, a command is incorrectly formatted, or a serious program error occurs. *

* * @param reply The reply code to test. * @return True if a reply code is a negative permanent response, false if not. */ public static boolean isNegativePermanent(final int reply) { return reply >= 500 && reply < 600; } /** * Determine if a reply code is a negative transient response. All codes beginning with a 4 are negative transient responses. The NNTP server will send a * negative transient response on the failure of a correctly formatted command that could not be performed for some reason. For example, retrieving an * article that does not exist will result in a negative transient response. *

* * @param reply The reply code to test. * @return True if a reply code is a negative transient response, false if not. */ public static boolean isNegativeTransient(final int reply) { return reply >= 400 && reply < 500; } /** * Determine if a reply code is a positive completion response. All codes beginning with a 2 are positive completion responses. The NNTP server will send a * positive completion response on the final successful completion of a command. *

* * @param reply The reply code to test. * @return True if a reply code is a positive completion response, false if not. */ public static boolean isPositiveCompletion(final int reply) { return reply >= 200 && reply < 300; } /** * Determine if a reply code is a positive intermediate response. All codes beginning with a 3 are positive intermediate responses. The NNTP server will * send a positive intermediate response on the successful completion of one part of a multi-part command or sequence of commands. For example, after a * successful POST command, a positive intermediate response will be sent to indicate that the server is ready to receive the article to be posted. *

* * @param reply The reply code to test. * @return True if a reply code is a positive intermediate response, false if not. */ public static boolean isPositiveIntermediate(final int reply) { return reply >= 300 && reply < 400; } private NNTPReply() { } } NewGroupsOrNewsQuery.java000066400000000000000000000200361434047722200335720ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; import java.util.Calendar; /** * The NewGroupsOrNewsQuery class. This is used to issue NNTP NEWGROUPS and NEWNEWS queries, implemented by * {@link org.apache.commons.net.nntp.NNTPClient#listNewNewsgroups listNewNewsGroups } and {@link org.apache.commons.net.nntp.NNTPClient#listNewNews listNewNews * } respectively. It prevents you from having to format date, time, distribution, and newgroup arguments. *

* You might use the class as follows: * *

 * query = new NewsGroupsOrNewsQuery(new GregorianCalendar(97, 11, 15), false);
 * query.addDistribution("comp");
 * NewsgroupInfo[] newsgroups = client.listNewgroups(query);
 * 
* * This will retrieve the list of newsgroups starting with the comp. distribution prefix created since midnight 11/15/97. * * @see NNTPClient */ public final class NewGroupsOrNewsQuery { private final String date, time; private StringBuffer distributions; private StringBuffer newsgroups; private final boolean isGMT; /** * Creates a new query using the given time as a reference point. *

* * @param date The date since which new groups or news have arrived. * @param gmt True if the date should be considered as GMT, false if not. */ public NewGroupsOrNewsQuery(final Calendar date, final boolean gmt) { int num; String str; final StringBuilder buffer; this.distributions = null; this.newsgroups = null; this.isGMT = gmt; buffer = new StringBuilder(); // Get year num = date.get(Calendar.YEAR); str = Integer.toString(num); num = str.length(); if (num >= 2) { buffer.append(str.substring(num - 2)); } else { buffer.append("00"); } // Get month num = date.get(Calendar.MONTH) + 1; str = Integer.toString(num); num = str.length(); if (num == 1) { buffer.append('0'); buffer.append(str); } else if (num == 2) { buffer.append(str); } else { buffer.append("01"); } // Get day num = date.get(Calendar.DAY_OF_MONTH); str = Integer.toString(num); num = str.length(); if (num == 1) { buffer.append('0'); buffer.append(str); } else if (num == 2) { buffer.append(str); } else { buffer.append("01"); } this.date = buffer.toString(); buffer.setLength(0); // Get hour num = date.get(Calendar.HOUR_OF_DAY); str = Integer.toString(num); num = str.length(); if (num == 1) { buffer.append('0'); buffer.append(str); } else if (num == 2) { buffer.append(str); } else { buffer.append("00"); } // Get minutes num = date.get(Calendar.MINUTE); str = Integer.toString(num); num = str.length(); if (num == 1) { buffer.append('0'); buffer.append(str); } else if (num == 2) { buffer.append(str); } else { buffer.append("00"); } // Get seconds num = date.get(Calendar.SECOND); str = Integer.toString(num); num = str.length(); if (num == 1) { buffer.append('0'); buffer.append(str); } else if (num == 2) { buffer.append(str); } else { buffer.append("00"); } this.time = buffer.toString(); } /** * Add a distribution group to the query. The distribution part of a newsgroup is the segment of the name preceding the first dot (e.g., comp, alt, rec). * Only those newsgroups matching one of the distributions or, in the case of NEWNEWS, an article in a newsgroup matching one of the distributions, will be * reported as a query result. Adding distributions is purely optional. *

* * @param distribution A distribution to add to the query. */ public void addDistribution(final String distribution) { if (distributions != null) { distributions.append(','); } else { distributions = new StringBuffer(); } distributions.append(distribution); } /** * Add a newsgroup to the list of newsgroups being queried. Newsgroups added this way are only meaningful to the NEWNEWS command. Newsgroup names may * include the * wildcard, as in comp.lang.* or comp.lang.java.* . Adding at least one newsgroup is mandatory for * the NEWNEWS command. *

* * @param newsgroup The newsgroup to add to the list of groups to be checked for new news. */ public void addNewsgroup(final String newsgroup) { if (newsgroups != null) { newsgroups.append(','); } else { newsgroups = new StringBuffer(); } newsgroups.append(newsgroup); } /** * Return the NNTP query formatted date (year, month, day in the form YYMMDD. *

* * @return The NNTP query formatted date. */ public String getDate() { return date; } /** * Return the comma separated list of distributions. This may be null if there are no distributions. *

* * @return The list of distributions, which may be null if no distributions have been specified. */ public String getDistributions() { return distributions == null ? null : distributions.toString(); } /** * Return the comma separated list of newsgroups. This may be null if there are no newsgroups *

* * @return The list of newsgroups, which may be null if no newsgroups have been specified. */ public String getNewsgroups() { return newsgroups == null ? null : newsgroups.toString(); } /** * Return the NNTP query formatted time (hour, minutes, seconds in the form HHMMSS. *

* * @return The NNTP query formatted time. */ public String getTime() { return time; } /** * Return whether or not the query date should be treated as GMT. *

* * @return True if the query date is to be treated as GMT, false if not. */ public boolean isGMT() { return isGMT; } /** * Add a newsgroup to the list of newsgroups being queried, but indicate that group should not be checked for new news. Newsgroups added this way are only * meaningful to the NEWNEWS command. Newsgroup names may include the * wildcard, as in comp.lang.* or * comp.lang.java.* . *

* The following would create a query that searched for new news in all comp.lang.java newsgroups except for comp.lang.java.advocacy. * *

     * query.addNewsgroup("comp.lang.java.*");
     * query.omitNewsgroup("comp.lang.java.advocacy");
     * 
*

* * @param newsgroup The newsgroup to add to the list of groups to be checked for new news, but which should be omitted from the search for new news.. */ public void omitNewsgroup(final String newsgroup) { addNewsgroup("!" + newsgroup); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/NewsgroupInfo.java000066400000000000000000000111561434047722200323440ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * NewsgroupInfo stores information pertaining to a newsgroup returned by the NNTP GROUP, LIST, and NEWGROUPS commands, implemented by * {@link org.apache.commons.net.nntp.NNTPClient#selectNewsgroup selectNewsgroup } , {@link org.apache.commons.net.nntp.NNTPClient#listNewsgroups listNewsgroups * } , and {@link org.apache.commons.net.nntp.NNTPClient#listNewNewsgroups listNewNewsgroups } respectively. * * @see NNTPClient */ public final class NewsgroupInfo { /** * A constant indicating that the posting permission of a newsgroup is unknown. For example, the NNTP GROUP command does not return posting information, so * NewsgroupInfo instances obtained from that command willhave an UNKNOWN_POSTING_PERMISSION. */ public static final int UNKNOWN_POSTING_PERMISSION = 0; /** A constant indicating that a newsgroup is moderated. */ public static final int MODERATED_POSTING_PERMISSION = 1; /** A constant indicating that a newsgroup is public and unmoderated. */ public static final int PERMITTED_POSTING_PERMISSION = 2; /** * A constant indicating that a newsgroup is closed for general posting. */ public static final int PROHIBITED_POSTING_PERMISSION = 3; private String newsgroup; private long estimatedArticleCount; private long firstArticle; private long lastArticle; private int postingPermission; @Deprecated public int getArticleCount() { return (int) estimatedArticleCount; } /** * Get the estimated number of articles in the newsgroup. The accuracy of this value will depend on the server implementation. *

* * @return The estimated number of articles in the newsgroup. */ public long getArticleCountLong() { return estimatedArticleCount; } @Deprecated public int getFirstArticle() { return (int) firstArticle; } /** * Get the number of the first article in the newsgroup. *

* * @return The number of the first article in the newsgroup. */ public long getFirstArticleLong() { return firstArticle; } @Deprecated public int getLastArticle() { return (int) lastArticle; } /** * Get the number of the last article in the newsgroup. *

* * @return The number of the last article in the newsgroup. */ public long getLastArticleLong() { return lastArticle; } /** * Get the newsgroup name. *

* * @return The name of the newsgroup. */ public String getNewsgroup() { return newsgroup; } /** * Get the posting permission of the newsgroup. This will be one of the POSTING_PERMISSION constants. *

* * @return The posting permission status of the newsgroup. */ public int getPostingPermission() { return postingPermission; } void setArticleCount(final long count) { estimatedArticleCount = count; } void setFirstArticle(final long first) { firstArticle = first; } /* * public String toString() { StringBuilder buffer = new StringBuilder(); buffer.append(__newsgroup); buffer.append(' '); buffer.append(__lastArticle); * buffer.append(' '); buffer.append(__firstArticle); buffer.append(' '); switch(__postingPermission) { case 1: buffer.append('m'); break; case 2: * buffer.append('y'); break; case 3: buffer.append('n'); break; } return buffer.toString(); } */ // DEPRECATED METHODS - for API compatibility only - DO NOT USE void setLastArticle(final long last) { lastArticle = last; } void setNewsgroup(final String newsgroup) { this.newsgroup = newsgroup; } void setPostingPermission(final int permission) { postingPermission = permission; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/NewsgroupIterator.java000066400000000000000000000033161434047722200332410ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.nntp; import java.util.Iterator; /** * Class which wraps an {@code Iterable} of raw newgroup information to generate an {@code Iterable} of the parsed information. * * @since 3.0 */ class NewsgroupIterator implements Iterator, Iterable { private final Iterator stringIterator; public NewsgroupIterator(final Iterable iterableString) { stringIterator = iterableString.iterator(); } @Override public boolean hasNext() { return stringIterator.hasNext(); } @Override public Iterator iterator() { return this; } @Override public NewsgroupInfo next() { final String line = stringIterator.next(); return NNTPClient.parseNewsgroupListEntry(line); } @Override public void remove() { stringIterator.remove(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/ReplyIterator.java000066400000000000000000000060271434047722200323450ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.nntp; import java.io.BufferedReader; import java.io.IOException; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.commons.net.io.DotTerminatedMessageReader; import org.apache.commons.net.io.Util; /** * Wraps a {@link BufferedReader} and returns an {@code Iterable} which returns the individual lines from the reader. * * @since 3.0 */ class ReplyIterator implements Iterator, Iterable { private final BufferedReader reader; private String line; private Exception savedException; ReplyIterator(final BufferedReader _reader) throws IOException { this(_reader, true); } /** * * @param _reader the reader to wrap * @param addDotReader whether to additionally wrap the reader in a DotTerminatedMessageReader * @throws IOException */ ReplyIterator(final BufferedReader _reader, final boolean addDotReader) throws IOException { reader = addDotReader ? new DotTerminatedMessageReader(_reader) : _reader; line = reader.readLine(); // prime the iterator if (line == null) { Util.closeQuietly(reader); } } @Override public boolean hasNext() { if (savedException != null) { throw new NoSuchElementException(savedException.toString()); } return line != null; } @Override public Iterator iterator() { return this; } @Override public String next() throws NoSuchElementException { if (savedException != null) { throw new NoSuchElementException(savedException.toString()); } final String prev = line; if (prev == null) { throw new NoSuchElementException(); } try { line = reader.readLine(); // save next line if (line == null) { Util.closeQuietly(reader); } } catch (final IOException ex) { savedException = ex; // if it fails, save the exception, as it does not apply to this call Util.closeQuietly(reader); } return prev; } @Override public void remove() { throw new UnsupportedOperationException(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/SimpleNNTPHeader.java000066400000000000000000000121641434047722200326010ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * This class is used to construct the bare minimum acceptable header for most news readers. To construct more complicated headers you should refer to RFC 822. * When the Java Mail API is finalized, you will be able to use it to compose fully compliant Internet text messages. *

* The main purpose of the class is to faciliatate the article posting process, by relieving the programmer from having to explicitly format an article header. * For example: * *

 * writer = client.postArticle();
 * if (writer == null) // failure
 *     return false;
 * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
 * header.addNewsgroup("alt.test");
 * header.addHeaderField("Organization", "Foobar, Inc.");
 * writer.write(header.toString());
 * writer.write("This is just a test");
 * writer.close();
 * if (!client.completePendingCommand()) // failure
 *     return false;
 * 
* * @see NNTPClient */ public class SimpleNNTPHeader { private final String subject, from; private final StringBuilder newsgroups; private final StringBuilder headerFields; private int newsgroupCount; /** * Creates a new SimpleNNTPHeader instance initialized with the given from and subject header field values. *

* * @param from The value of the From: header field. This should be the article poster's email address. * @param subject The value of the Subject: header field. This should be the subject of the article. */ public SimpleNNTPHeader(final String from, final String subject) { this.from = from; this.subject = subject; this.newsgroups = new StringBuilder(); this.headerFields = new StringBuilder(); this.newsgroupCount = 0; } /** * Adds an arbitrary header field with the given value to the article header. These headers will be written after the From, Newsgroups, and Subject fields * when the SimpleNNTPHeader is convertered to a string. An example use would be: * *

     * header.addHeaderField("Organization", "Foobar, Inc.");
     * 
*

* * @param headerField The header field to add, not including the colon. * @param value The value of the added header field. */ public void addHeaderField(final String headerField, final String value) { headerFields.append(headerField); headerFields.append(": "); headerFields.append(value); headerFields.append('\n'); } /** * Adds a newsgroup to the article Newsgroups: field. *

* * @param newsgroup The newsgroup to add to the article's newsgroup distribution list. */ public void addNewsgroup(final String newsgroup) { if (newsgroupCount++ > 0) { newsgroups.append(','); } newsgroups.append(newsgroup); } /** * Returns the address used in the From: header field. *

* * @return The from address. */ public String getFromAddress() { return from; } /** * Returns the contents of the Newsgroups: header field. *

* * @return The comma-separated list of newsgroups to which the article is being posted. */ public String getNewsgroups() { return newsgroups.toString(); } /** * Returns the subject used in the Subject: header field. *

* * @return The subject. */ public String getSubject() { return subject; } /** * Converts the SimpleNNTPHeader to a properly formatted header in the form of a String, including the blank line used to separate the header from the * article body. *

* * @return The article header in the form of a String. */ @Override public String toString() { final StringBuilder header = new StringBuilder(); header.append("From: "); header.append(from); header.append("\nNewsgroups: "); header.append(newsgroups.toString()); header.append("\nSubject: "); header.append(subject); header.append('\n'); if (headerFields.length() > 0) { header.append(headerFields.toString()); } header.append('\n'); return header.toString(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/ThreadContainer.java000066400000000000000000000060501434047722200326060ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * A placeholder utility class, used for constructing a tree of Threadables Original implementation by Jamie Zawinski. See the Grendel source for more details * here Threadable objects */ class ThreadContainer { Threadable threadable; ThreadContainer parent; // ThreadContainer prev; ThreadContainer next; ThreadContainer child; /** * * @param target * @return true if child is under self's tree. Detects circular references */ boolean findChild(final ThreadContainer target) { if (child == null) { return false; } if (child == target) { return true; } return child.findChild(target); } // Copy the ThreadContainer tree structure down into the underlying Threadable objects // (Make the Threadable tree look like the ThreadContainer tree) // TODO convert this to an iterative function - this can blow the stack // with very large Threadable trees void flush() { if (parent != null && threadable == null) { throw new RuntimeException("no threadable in " + this.toString()); } parent = null; if (threadable != null) { threadable.setChild(child == null ? null : child.threadable); } if (child != null) { child.flush(); child = null; } if (threadable != null) { threadable.setNext(next == null ? null : next.threadable); } if (next != null) { next.flush(); next = null; } threadable = null; } /** * Reverse the entire set of children * */ void reverseChildren() { if (child != null) { ThreadContainer kid, prev, rest; for (prev = null, kid = child, rest = kid.next; kid != null; prev = kid, kid = rest, rest = rest == null ? null : rest.next) { kid.next = prev; } child = prev; // Do it for the kids for (kid = child; kid != null; kid = kid.next) { kid.reverseChildren(); } } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/Threadable.java000066400000000000000000000023431434047722200315700ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * A placeholder interface for threadable message objects Author: Rory Winston (rwinston@checkfree.com) * */ public interface Threadable { boolean isDummy(); Threadable makeDummy(); String messageThreadId(); String[] messageThreadReferences(); void setChild(Threadable child); void setNext(Threadable next); String simplifiedSubject(); boolean subjectIsReply(); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/Threader.java000066400000000000000000000404561434047722200313020ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; /** * This is an implementation of a message threading algorithm, as originally devised by Zamie Zawinski. * See http://www.jwz.org/doc/threading.html for details. * For his Java implementation, see * * http://lxr.mozilla.org/mozilla/source/grendel/sources/grendel/view/Threader.java */ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Threader { /** * * @param threadable * @param idTable */ private void buildContainer(final Threadable threadable, final HashMap idTable) { String id = threadable.messageThreadId(); ThreadContainer container = idTable.get(id); int bogusIdCount = 0; // A ThreadContainer exists for this id already. This should be a forward reference, but may // be a duplicate id, in which case we will need to generate a bogus placeholder id if (container != null) { if (container.threadable != null) { // oops! duplicate ids... bogusIdCount++; // Avoid dead local store warning id = ""; container = null; } else { // The container just contained a forward reference to this message, so let's // fill in the threadable field of the container with this message container.threadable = threadable; } } // No container exists for that message Id. Create one and insert it into the hash table. if (container == null) { container = new ThreadContainer(); container.threadable = threadable; idTable.put(id, container); } // Iterate through all of the references and create ThreadContainers for any references that // don't have them. ThreadContainer parentRef = null; { final String[] references = threadable.messageThreadReferences(); for (final String refString : references) { ThreadContainer ref = idTable.get(refString); // if this id doesnt have a container, create one if (ref == null) { ref = new ThreadContainer(); idTable.put(refString, ref); } // Link references together in the order they appear in the References: header, // IF they dont have a have a parent already && // IF it will not cause a circular reference if ((parentRef != null) && (ref.parent == null) && (parentRef != ref) && !(ref.findChild(parentRef))) { // Link ref into the parent's child list ref.parent = parentRef; ref.next = parentRef.child; parentRef.child = ref; } parentRef = ref; } } // parentRef is now set to the container of the last element in the references field. make that // be the parent of this container, unless doing so causes a circular reference if (parentRef != null && (parentRef == container || container.findChild(parentRef))) { parentRef = null; } // if it has a parent already, its because we saw this message in a References: field, and presumed // a parent based on the other entries in that field. Now that we have the actual message, we can // throw away the old parent and use this new one if (container.parent != null) { ThreadContainer rest, prev; for (prev = null, rest = container.parent.child; rest != null; prev = rest, rest = rest.next) { if (rest == container) { break; } } if (rest == null) { throw new RuntimeException("Didnt find " + container + " in parent" + container.parent); } // Unlink this container from the parent's child list if (prev == null) { container.parent.child = container.next; } else { prev.next = container.next; } container.next = null; container.parent = null; } // If we have a parent, link container into the parents child list if (parentRef != null) { container.parent = parentRef; container.next = parentRef.child; parentRef.child = container; } } /** * Find the root set of all existing ThreadContainers * * @param idTable * @return root the ThreadContainer representing the root node */ private ThreadContainer findRootSet(final HashMap idTable) { final ThreadContainer root = new ThreadContainer(); for (final Map.Entry entry : idTable.entrySet()) { final ThreadContainer c = entry.getValue(); if (c.parent == null) { if (c.next != null) { throw new RuntimeException("c.next is " + c.next.toString()); } c.next = root.child; root.child = c; } } return root; } /** * If any two members of the root set have the same subject, merge them. This is to attempt to accomodate messages without References: headers. * * @param root */ private void gatherSubjects(final ThreadContainer root) { int count = 0; for (ThreadContainer c = root.child; c != null; c = c.next) { count++; } // TODO verify this will avoid rehashing HashMap subjectTable = new HashMap<>((int) (count * 1.2), (float) 0.9); count = 0; for (ThreadContainer c = root.child; c != null; c = c.next) { Threadable threadable = c.threadable; // No threadable? If so, it is a dummy node in the root set. // Only root set members may be dummies, and they alway have at least 2 kids // Take the first kid as representative of the subject if (threadable == null) { threadable = c.child.threadable; } final String subj = threadable.simplifiedSubject(); if (subj == null || subj.isEmpty()) { continue; } final ThreadContainer old = subjectTable.get(subj); // Add this container to the table iff: // - There exists no container with this subject // - or this is a dummy container and the old one is not - the dummy one is // more interesting as a root, so put it in the table instead // - The container in the table has a "Re:" version of this subject, and // this container has a non-"Re:" version of this subject. The non-"Re:" version // is the more interesting of the two. if (old == null || (c.threadable == null && old.threadable != null) || (old.threadable != null && old.threadable.subjectIsReply() && c.threadable != null && !c.threadable.subjectIsReply())) { subjectTable.put(subj, c); count++; } } // If the table is empty, we're done if (count == 0) { return; } // subjectTable is now populated with one entry for each subject which occurs in the // root set. Iterate over the root set, and gather together the difference. ThreadContainer prev, c, rest; for (prev = null, c = root.child, rest = c.next; c != null; prev = c, c = rest, rest = (rest == null ? null : rest.next)) { Threadable threadable = c.threadable; // is it a dummy node? if (threadable == null) { threadable = c.child.threadable; } final String subj = threadable.simplifiedSubject(); // Dont thread together all subjectless messages if (subj == null || subj.isEmpty()) { continue; } final ThreadContainer old = subjectTable.get(subj); if (old == c) { // That's us continue; } // We have now found another container in the root set with the same subject // Remove the "second" message from the root set if (prev == null) { root.child = c.next; } else { prev.next = c.next; } c.next = null; if (old.threadable == null && c.threadable == null) { // both dummies - merge them ThreadContainer tail; for (tail = old.child; tail != null && tail.next != null; tail = tail.next) { // do nothing } if (tail != null) { // protect against possible NPE tail.next = c.child; } for (tail = c.child; tail != null; tail = tail.next) { tail.parent = old; } c.child = null; } else if (old.threadable == null || (c.threadable != null && c.threadable.subjectIsReply() && !old.threadable.subjectIsReply())) { // Else if old is empty, or c has "Re:" and old does not ==> make this message a child of old c.parent = old; c.next = old.child; old.child = c; } else { // else make the old and new messages be children of a new dummy container. // We create a new container object for old.msg and empty the old container final ThreadContainer newc = new ThreadContainer(); newc.threadable = old.threadable; newc.child = old.child; for (ThreadContainer tail = newc.child; tail != null; tail = tail.next) { tail.parent = newc; } old.threadable = null; old.child = null; c.parent = old; newc.parent = old; // Old is now a dummy- give it 2 kids , c and newc old.child = c; c.next = newc; } // We've done a merge, so keep the same prev c = prev; } subjectTable.clear(); subjectTable = null; } /** * Delete any empty or dummy ThreadContainers * * @param parent */ private void pruneEmptyContainers(final ThreadContainer parent) { ThreadContainer container, prev, next; for (prev = null, container = parent.child, next = container.next; container != null; prev = container, container = next, next = (container == null ? null : container.next)) { // Is it empty and without any children? If so,delete it if (container.threadable == null && container.child == null) { if (prev == null) { parent.child = container.next; } else { prev.next = container.next; } // Set container to prev so that prev keeps its same value the next time through the loop container = prev; } // Else if empty, with kids, and (not at root or only one kid) else if (container.threadable == null && (container.parent != null || container.child.next == null)) { // We have an invalid/expired message with kids. Promote the kids to this level. ThreadContainer tail; final ThreadContainer kids = container.child; // Remove this container and replace with 'kids'. if (prev == null) { parent.child = kids; } else { prev.next = kids; } // Make each child's parent be this level's parent -> i.e. promote the children. // Make the last child's next point to this container's next // i.e. splice kids into the list in place of container for (tail = kids; tail.next != null; tail = tail.next) { tail.parent = container.parent; } tail.parent = container.parent; tail.next = container.next; // next currently points to the item after the inserted items in the chain - reset that so we process the newly // promoted items next time round next = kids; // Set container to prev so that prev keeps its same value the next time through the loop container = prev; } else if (container.child != null) { // A real message , with kids // Iterate over the children pruneEmptyContainers(container); } } } /** * The client passes in a list of Iterable objects, and the Threader constructs a connected 'graph' of messages * * @param messages iterable of messages to thread, must not be empty * @return null if messages == null or root.child == null or messages list is empty * @since 3.0 */ public Threadable thread(final Iterable messages) { if (messages == null) { return null; } HashMap idTable = new HashMap<>(); // walk through each Threadable element for (final Threadable t : messages) { if (!t.isDummy()) { buildContainer(t, idTable); } } if (idTable.isEmpty()) { return null; } final ThreadContainer root = findRootSet(idTable); idTable.clear(); idTable = null; pruneEmptyContainers(root); root.reverseChildren(); gatherSubjects(root); if (root.next != null) { throw new RuntimeException("root node has a next:" + root); } for (ThreadContainer r = root.child; r != null; r = r.next) { if (r.threadable == null) { r.threadable = r.child.threadable.makeDummy(); } } final Threadable result = (root.child == null ? null : root.child.threadable); root.flush(); return result; } /** * The client passes in a list of Threadable objects, and the Threader constructs a connected 'graph' of messages * * @param messages list of messages to thread, must not be empty * @return null if messages == null or root.child == null or messages list is empty * @since 2.2 */ public Threadable thread(final List messages) { return thread((Iterable) messages); } // DEPRECATED METHODS - for API compatibility only - DO NOT USE /** * The client passes in an array of Threadable objects, and the Threader constructs a connected 'graph' of messages * * @param messages array of messages to thread, must not be empty * @return null if messages == null or root.child == null or messages array is empty * @deprecated (2.2) prefer {@link #thread(List)} */ @Deprecated public Threadable thread(final Threadable[] messages) { if (messages == null) { return null; } return thread(Arrays.asList(messages)); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/nntp/package-info.java000066400000000000000000000015711434047722200320630ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * NNTP - network news transfer protocol */ package org.apache.commons.net.nntp;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/000077500000000000000000000000001434047722200265125ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/NTPUDPClient.java000066400000000000000000000126441434047722200315350ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import org.apache.commons.net.DatagramSocketClient; /** * The NTPUDPClient class is a UDP implementation of a client for the Network Time Protocol (NTP) described in RFC 1305 as well as the Simple Network Time * Protocol (SNTP) in RFC-2030. To use the class, merely open a local datagram socket with open and call getTime * to retrieve the time. Then call close to close the connection properly. Successive * calls to getTime are permitted without re-establishing a connection. That is because UDP is a connectionless protocol and the * Network Time Protocol is stateless. * */ public final class NTPUDPClient extends DatagramSocketClient { /** The default NTP port. It is set to 123 according to RFC 1305. */ public static final int DEFAULT_PORT = 123; private int version = NtpV3Packet.VERSION_3; /** * Retrieves the time information from the specified server on the default NTP port and returns it. The time is the number of miliiseconds since 00:00 * (midnight) 1 January 1900 UTC, as specified by RFC 1305. This method reads the raw NTP packet and constructs a TimeInfo object that allows access * to all the fields of the NTP message header. *

* * @param host The address of the server. * @return The time value retrieved from the server. * @throws IOException If an error occurs while retrieving the time. */ public TimeInfo getTime(final InetAddress host) throws IOException { return getTime(host, NtpV3Packet.NTP_PORT); } /** * Retrieves the time information from the specified server and port and returns it. The time is the number of miliiseconds since 00:00 (midnight) 1 January * 1900 UTC, as specified by RFC 1305. This method reads the raw NTP packet and constructs a TimeInfo object that allows access to all the fields of * the NTP message header. *

* * @param host The address of the server. * @param port The port of the service. * @return The time value retrieved from the server. * @throws IOException If an error occurs while retrieving the time or if received packet does not match the request. */ public TimeInfo getTime(final InetAddress host, final int port) throws IOException { // if not connected then open to next available UDP port if (!isOpen()) { open(); } final NtpV3Packet message = new NtpV3Impl(); message.setMode(NtpV3Packet.MODE_CLIENT); message.setVersion(version); final DatagramPacket sendPacket = message.getDatagramPacket(); sendPacket.setAddress(host); sendPacket.setPort(port); final NtpV3Packet recMessage = new NtpV3Impl(); final DatagramPacket receivePacket = recMessage.getDatagramPacket(); /* * Must minimize the time between getting the current time, timestamping the packet, and sending it out which introduces an error in the delay time. No * extraneous logging and initializations here !!! */ final TimeStamp now = TimeStamp.getCurrentTime(); // Note that if you do not set the transmit time field then originating time // in server response is all 0's which is "Thu Feb 07 01:28:16 EST 2036". message.setTransmitTime(now); _socket_.send(sendPacket); _socket_.receive(receivePacket); final long returnTimeMillis = System.currentTimeMillis(); // Prevent invalid time information if response does not match request if (!now.equals(recMessage.getOriginateTimeStamp())) { throw new IOException("Originate time does not match the request"); } // create TimeInfo message container but don't pre-compute the details yet return new TimeInfo(recMessage, returnTimeMillis, false); } /** * Returns the NTP protocol version number that client sets on request packet that is sent to remote host (e.g. 3=NTP v3, 4=NTP v4, etc.) * * @return the NTP protocol version number that client sets on request packet. * @see #setVersion(int) */ public int getVersion() { return version; } /** * Sets the NTP protocol version number that client sets on request packet communicate with remote host. * * @param version the NTP protocol version number */ public void setVersion(final int version) { this.version = version; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/NtpUtils.java000066400000000000000000000072671434047722200311530ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; /** * Common NtpUtils Helper class. * */ public final class NtpUtils { /** * Returns 32-bit integer address to IPv4 address string "%d.%d.%d.%d" format. * * @param address the 32-bit address * @return the raw IP address in a string format. */ public static String getHostAddress(final int address) { return ((address >>> 24) & 0xFF) + "." + ((address >>> 16) & 0xFF) + "." + ((address >>> 8) & 0xFF) + "." + ((address >>> 0) & 0xFF); } /** * Return human-readable name of message mode type (RFC 1305). * * @param mode the mode type * @return mode name */ public static String getModeName(final int mode) { switch (mode) { case NtpV3Packet.MODE_RESERVED: return "Reserved"; case NtpV3Packet.MODE_SYMMETRIC_ACTIVE: return "Symmetric Active"; case NtpV3Packet.MODE_SYMMETRIC_PASSIVE: return "Symmetric Passive"; case NtpV3Packet.MODE_CLIENT: return "Client"; case NtpV3Packet.MODE_SERVER: return "Server"; case NtpV3Packet.MODE_BROADCAST: return "Broadcast"; case NtpV3Packet.MODE_CONTROL_MESSAGE: return "Control"; case NtpV3Packet.MODE_PRIVATE: return "Private"; default: return "Unknown"; } } /** * Returns NTP packet reference identifier as IP address. * * @param packet NTP packet * @return the packet reference id (as IP address) in "%d.%d.%d.%d" format. */ public static String getRefAddress(final NtpV3Packet packet) { final int address = (packet == null) ? 0 : packet.getReferenceId(); return getHostAddress(address); } /** * Get refId as reference clock string (e.g. GPS, WWV, LCL). If string is invalid (non-ASCII character) then returns empty string "". For details refer to * the Comprehensive List of Clock Drivers. * * @param message the message to check * @return reference clock string if primary NTP server */ public static String getReferenceClock(final NtpV3Packet message) { if (message == null) { return ""; } final int refId = message.getReferenceId(); if (refId == 0) { return ""; } final StringBuilder buf = new StringBuilder(4); // start at highest-order byte (0x4c434c00 -> LCL) for (int shiftBits = 24; shiftBits >= 0; shiftBits -= 8) { final char c = (char) ((refId >>> shiftBits) & 0xff); if (c == 0) { // 0-terminated ASCII string break; } if (!Character.isLetterOrDigit(c)) { return ""; } buf.append(c); } return buf.toString(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/NtpV3Impl.java000066400000000000000000000442651434047722200311640ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import java.net.DatagramPacket; /** * Implementation of NtpV3Packet with methods converting Java objects to/from the Network Time Protocol (NTP) data message header format described in RFC-1305. * */ public class NtpV3Impl implements NtpV3Packet { private static final int MODE_INDEX = 0; private static final int MODE_SHIFT = 0; private static final int VERSION_INDEX = 0; private static final int VERSION_SHIFT = 3; private static final int LI_INDEX = 0; private static final int LI_SHIFT = 6; private static final int STRATUM_INDEX = 1; private static final int POLL_INDEX = 2; private static final int PRECISION_INDEX = 3; private static final int ROOT_DELAY_INDEX = 4; private static final int ROOT_DISPERSION_INDEX = 8; private static final int REFERENCE_ID_INDEX = 12; private static final int REFERENCE_TIMESTAMP_INDEX = 16; private static final int ORIGINATE_TIMESTAMP_INDEX = 24; private static final int RECEIVE_TIMESTAMP_INDEX = 32; private static final int TRANSMIT_TIMESTAMP_INDEX = 40; // private static final int KEY_IDENTIFIER_INDEX = 48; // private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */ /** * Convert byte to unsigned integer. Java only has signed types so we have to do more work to get unsigned ops. * * @param b input byte * @return unsigned int value of byte */ protected static final int ui(final byte b) { final int i = b & 0xFF; return i; } /** * Convert byte to unsigned long. Java only has signed types so we have to do more work to get unsigned ops * * @param b input byte * @return unsigned long value of byte */ protected static final long ul(final byte b) { final long i = b & 0xFF; return i; } private final byte[] buf = new byte[48]; private volatile DatagramPacket dp; /** Creates a new instance of NtpV3Impl */ public NtpV3Impl() { } /** * Compares this object against the specified object. The result is true if and only if the argument is not null and is a * NtpV3Impl object that contains the same values as this object. * * @param obj the object to compare with. * @return true if the objects are the same; false otherwise. * @since 3.4 */ @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final NtpV3Impl other = (NtpV3Impl) obj; return java.util.Arrays.equals(buf, other.buf); } /** * Returns the datagram packet with the NTP details already filled in. * * @return a datagram packet. */ @Override public synchronized DatagramPacket getDatagramPacket() { if (dp == null) { dp = new DatagramPacket(buf, buf.length); dp.setPort(NTP_PORT); } return dp; } /** * @return 4 bytes as 32-bit int */ private int getInt(final int index) { final int i = ui(buf[index]) << 24 | ui(buf[index + 1]) << 16 | ui(buf[index + 2]) << 8 | ui(buf[index + 3]); return i; } /** * Returns leap indicator as defined in RFC-1305 which is a two-bit code: 0=no warning 1=last minute has 61 seconds 2=last minute has 59 seconds 3=alarm * condition (clock not synchronized) * * @return leap indicator as defined in RFC-1305. */ @Override public int getLeapIndicator() { return (ui(buf[LI_INDEX]) >> LI_SHIFT) & 0x3; } /** * Get Long value represented by bits starting at specified index. * * @return 8 bytes as 64-bit long */ private long getLong(final int index) { final long i = ul(buf[index]) << 56 | ul(buf[index + 1]) << 48 | ul(buf[index + 2]) << 40 | ul(buf[index + 3]) << 32 | ul(buf[index + 4]) << 24 | ul(buf[index + 5]) << 16 | ul(buf[index + 6]) << 8 | ul(buf[index + 7]); return i; } /** * Returns mode as defined in RFC-1305 which is a 3-bit integer whose value is indicated by the MODE_xxx parameters. * * @return mode as defined in RFC-1305. */ @Override public int getMode() { return (ui(buf[MODE_INDEX]) >> MODE_SHIFT) & 0x7; } /** * Return human-readable name of message mode type as described in RFC 1305. * * @return mode name as string. */ @Override public String getModeName() { return NtpUtils.getModeName(getMode()); } /** * Returns the originate time as defined in RFC-1305. * * @return the originate time. Never returns null. */ @Override public TimeStamp getOriginateTimeStamp() { return getTimestamp(ORIGINATE_TIMESTAMP_INDEX); } /** * Returns poll interval as defined in RFC-1305, which is an eight-bit signed integer indicating the maximum interval between successive messages, in * seconds to the nearest power of two (e.g. value of six indicates an interval of 64 seconds. The values that can appear in this field range from * NTP_MINPOLL to NTP_MAXPOLL inclusive. * * @return poll interval as defined in RFC-1305. */ @Override public int getPoll() { return buf[POLL_INDEX]; } /** * Returns precision as defined in RFC-1305 encoded as an 8-bit signed integer (seconds to nearest power of two). Values normally range from -6 to -20. * * @return precision as defined in RFC-1305. */ @Override public int getPrecision() { return buf[PRECISION_INDEX]; } /** * Returns receive timestamp as defined in RFC-1305. * * @return the receive time. Never returns null. */ @Override public TimeStamp getReceiveTimeStamp() { return getTimestamp(RECEIVE_TIMESTAMP_INDEX); } /** * Returns the reference id as defined in RFC-1305, which is a 32-bit integer whose value is dependent on several criteria. * * @return the reference id as defined in RFC-1305. */ @Override public int getReferenceId() { return getInt(REFERENCE_ID_INDEX); } /** * Returns the reference id string. String cannot be null but value is dependent on the version of the NTP spec supported and stratum level. Value can be an * empty string, clock type string, IP address, or a hex string. * * @return the reference id string. */ @Override public String getReferenceIdString() { final int version = getVersion(); final int stratum = getStratum(); if (version == VERSION_3 || version == VERSION_4) { if (stratum == 0 || stratum == 1) { return idAsString(); // 4-character ASCII string (e.g. GPS, USNO) } // in NTPv4 servers this is latest transmit timestamp of ref source if (version == VERSION_4) { return idAsHex(); } } // Stratum 2 and higher this is a four-octet IPv4 address // of the primary reference host. if (stratum >= 2) { return idAsIPAddress(); } return idAsHex(); } /** * Returns the reference time as defined in RFC-1305. * * @return the reference time as TimeStamp object. Never returns null. */ @Override public TimeStamp getReferenceTimeStamp() { return getTimestamp(REFERENCE_TIMESTAMP_INDEX); } /** * Return root delay as defined in RFC-1305, which is the total roundtrip delay to the primary reference source, in seconds. Values can take positive and * negative values, depending on clock precision and skew. * * @return root delay as defined in RFC-1305. */ @Override public int getRootDelay() { return getInt(ROOT_DELAY_INDEX); } /** * Return root delay as defined in RFC-1305 in milliseconds, which is the total roundtrip delay to the primary reference source, in seconds. Values can take * positive and negative values, depending on clock precision and skew. * * @return root delay in milliseconds */ @Override public double getRootDelayInMillisDouble() { final double l = getRootDelay(); return l / 65.536; } /** * Returns root dispersion as defined in RFC-1305. * * @return root dispersion. */ @Override public int getRootDispersion() { return getInt(ROOT_DISPERSION_INDEX); } /** * Returns root dispersion (as defined in RFC-1305) in milliseconds. * * @return root dispersion in milliseconds */ @Override public long getRootDispersionInMillis() { final long l = getRootDispersion(); return (l * 1000) / 65536L; } /** * Returns root dispersion (as defined in RFC-1305) in milliseconds as double precision value. * * @return root dispersion in milliseconds */ @Override public double getRootDispersionInMillisDouble() { final double l = getRootDispersion(); return l / 65.536; } /** * Returns Stratum as defined in RFC-1305, which indicates the stratum level of the local clock, with values defined as follows: 0=unspecified, 1=primary * ref clock, and all others a secondary reference (via NTP). * * @return Stratum level as defined in RFC-1305. */ @Override public int getStratum() { return ui(buf[STRATUM_INDEX]); } /** * Get NTP Timestamp at specified starting index. * * @param index index into data array * @return TimeStamp object for 64 bits starting at index */ private TimeStamp getTimestamp(final int index) { return new TimeStamp(getLong(index)); } /** * Returns the transmit timestamp as defined in RFC-1305. * * @return the transmit timestamp as defined in RFC-1305. Never returns a null object. */ @Override public TimeStamp getTransmitTimeStamp() { return getTimestamp(TRANSMIT_TIMESTAMP_INDEX); } /** * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...) correspond to the protocol used to obtain the timing information. * * @return packet type string identifier which in this case is "NTP". */ @Override public String getType() { return "NTP"; } /** * Returns NTP version number as defined in RFC-1305. * * @return NTP version number. */ @Override public int getVersion() { return (ui(buf[VERSION_INDEX]) >> VERSION_SHIFT) & 0x7; } /** * Computes a hashcode for this object. The result is the exclusive OR of the values of this object stored as a byte array. * * @return a hash code value for this object. * @since 3.4 */ @Override public int hashCode() { return java.util.Arrays.hashCode(buf); } private String idAsHex() { return Integer.toHexString(getReferenceId()); } /** * Returns Reference id as dotted IP address. * * @return refId as IP address string. */ private String idAsIPAddress() { return ui(buf[REFERENCE_ID_INDEX]) + "." + ui(buf[REFERENCE_ID_INDEX + 1]) + "." + ui(buf[REFERENCE_ID_INDEX + 2]) + "." + ui(buf[REFERENCE_ID_INDEX + 3]); } private String idAsString() { final StringBuilder id = new StringBuilder(); for (int i = 0; i <= 3; i++) { final char c = (char) buf[REFERENCE_ID_INDEX + i]; if (c == 0) { // 0-terminated string break; } id.append(c); } return id.toString(); } /** * Set the contents of this object from source datagram packet. * * @param srcDp source DatagramPacket to copy contents from, never null. * @throws IllegalArgumentException if srcDp is null or byte length is less than minimum length of 48 bytes */ @Override public void setDatagramPacket(final DatagramPacket srcDp) { if (srcDp == null || srcDp.getLength() < buf.length) { throw new IllegalArgumentException(); } final byte[] incomingBuf = srcDp.getData(); int len = srcDp.getLength(); if (len > buf.length) { len = buf.length; } System.arraycopy(incomingBuf, 0, buf, 0, len); final DatagramPacket dp = getDatagramPacket(); dp.setAddress(srcDp.getAddress()); final int port = srcDp.getPort(); dp.setPort(port > 0 ? port : NTP_PORT); dp.setData(buf); } /** * Set integer value at index position. * * @param idx index position * @param value 32-bit int value */ private void setInt(final int idx, int value) { for (int i = 3; i >= 0; i--) { buf[idx + i] = (byte) (value & 0xff); value >>>= 8; // shift right one-byte } } /** * Set leap indicator as defined in RFC-1305. * * @param li leap indicator. */ @Override public void setLeapIndicator(final int li) { buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | ((li & 0x3) << LI_SHIFT)); } /** * Set mode as defined in RFC-1305. * * @param mode the mode to set */ @Override public void setMode(final int mode) { buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7); } /** * Set originate timestamp given NTP TimeStamp object. If ts is null then zero time is used. * * @param ts NTP timestamp */ @Override public void setOriginateTimeStamp(final TimeStamp ts) { setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts); } /** * Set poll interval as defined in RFC-1305. * * @param poll poll interval. */ @Override public void setPoll(final int poll) { buf[POLL_INDEX] = (byte) (poll & 0xFF); } /** * Set precision as defined in RFC-1305. * * @param precision the precision to set * @since 3.4 */ @Override public void setPrecision(final int precision) { buf[PRECISION_INDEX] = (byte) (precision & 0xFF); } /** * Set receive timestamp given NTP TimeStamp object. If ts is null then zero time is used. * * @param ts timestamp */ @Override public void setReceiveTimeStamp(final TimeStamp ts) { setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts); } /** * Set reference clock identifier field with 32-bit unsigned integer value. See RFC-1305 for description. * * @param refId reference clock identifier. */ @Override public void setReferenceId(final int refId) { setInt(REFERENCE_ID_INDEX, refId); } /** * Set Reference time with NTP timestamp. If ts is null then zero time is used. * * @param ts NTP timestamp */ @Override public void setReferenceTime(final TimeStamp ts) { setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts); } /** * Set root delay as defined in RFC-1305. * * @param delay root delay * @since 3.4 */ @Override public void setRootDelay(final int delay) { setInt(ROOT_DELAY_INDEX, delay); } /** * Set root dispersion as defined in RFC-1305. * * @param dispersion root dispersion * @since 3.4 */ @Override public void setRootDispersion(final int dispersion) { setInt(ROOT_DISPERSION_INDEX, dispersion); } /** * Set stratum level as defined in RFC-1305. * * @param stratum stratum level. */ @Override public void setStratum(final int stratum) { buf[STRATUM_INDEX] = (byte) (stratum & 0xFF); } /** * Sets the NTP timestamp at the given array index. * * @param index index into the byte array. * @param t TimeStamp. */ private void setTimestamp(final int index, final TimeStamp t) { long ntpTime = (t == null) ? 0 : t.ntpValue(); // copy 64-bits from Long value into 8 x 8-bit bytes of array // one byte at a time shifting 8-bits for each position. for (int i = 7; i >= 0; i--) { buf[index + i] = (byte) (ntpTime & 0xFF); ntpTime >>>= 8; // shift to next byte } // buf[index] |= 0x80; // only set if 1900 baseline.... } /** * Set transmit time with NTP timestamp. If ts is null then zero time is used. * * @param ts NTP timestamp */ @Override public void setTransmitTime(final TimeStamp ts) { setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts); } /** * Set NTP version as defined in RFC-1305. * * @param version NTP version. */ @Override public void setVersion(final int version) { buf[VERSION_INDEX] = (byte) (buf[VERSION_INDEX] & 0xC7 | ((version & 0x7) << VERSION_SHIFT)); } /** * Returns details of NTP packet as a string. * * @return details of NTP packet as a string. */ @Override public String toString() { return "[" + "version:" + getVersion() + ", mode:" + getMode() + ", poll:" + getPoll() + ", precision:" + getPrecision() + ", delay:" + getRootDelay() + ", dispersion(ms):" + getRootDispersionInMillisDouble() + ", id:" + getReferenceIdString() + ", xmitTime:" + getTransmitTimeStamp().toDateString() + " ]"; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/NtpV3Packet.java000066400000000000000000000142641434047722200314660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import java.net.DatagramPacket; /** * Interface for a NtpV3Packet with get/set methods corresponding to the fields in the NTP Data Message Header described in RFC 1305. * */ public interface NtpV3Packet { /** * Standard NTP UDP port */ int NTP_PORT = 123; int LI_NO_WARNING = 0; int LI_LAST_MINUTE_HAS_61_SECONDS = 1; int LI_LAST_MINUTE_HAS_59_SECONDS = 2; int LI_ALARM_CONDITION = 3; /* mode options */ int MODE_RESERVED = 0; int MODE_SYMMETRIC_ACTIVE = 1; int MODE_SYMMETRIC_PASSIVE = 2; int MODE_CLIENT = 3; int MODE_SERVER = 4; int MODE_BROADCAST = 5; int MODE_CONTROL_MESSAGE = 6; int MODE_PRIVATE = 7; int NTP_MINPOLL = 4; // 16 seconds int NTP_MAXPOLL = 14; // 16284 seconds int NTP_MINCLOCK = 1; int NTP_MAXCLOCK = 10; int VERSION_3 = 3; int VERSION_4 = 4; /* * possible getType values such that other time-related protocols can have its information represented as NTP packets */ String TYPE_NTP = "NTP"; // RFC-1305/2030 String TYPE_ICMP = "ICMP"; // RFC-792 String TYPE_TIME = "TIME"; // RFC-868 String TYPE_DAYTIME = "DAYTIME"; // RFC-867 /** * @return a datagram packet with the NTP parts already filled in */ DatagramPacket getDatagramPacket(); /** * @return leap indicator as defined in RFC-1305 */ int getLeapIndicator(); /** * @return mode as defined in RFC-1305 */ int getMode(); /** * @return mode as human readable string; e.g. 3=Client */ String getModeName(); /** * @return the originate time as defined in RFC-1305 */ TimeStamp getOriginateTimeStamp(); /** * @return poll interval as defined in RFC-1305. Field range between NTP_MINPOLL and NTP_MAXPOLL. */ int getPoll(); /** * @return precision as defined in RFC-1305 */ int getPrecision(); /** * @return the receive time as defined in RFC-1305 */ TimeStamp getReceiveTimeStamp(); /** * @return the reference id (32-bit code) as defined in RFC-1305 */ int getReferenceId(); /** * @return the reference id string */ String getReferenceIdString(); /** * @return the reference time as defined in RFC-1305 */ TimeStamp getReferenceTimeStamp(); /** * @return root delay as defined in RFC-1305 */ int getRootDelay(); /** * @return root delay in milliseconds */ double getRootDelayInMillisDouble(); /** * @return root dispersion as defined in RFC-1305 */ int getRootDispersion(); /** * @return root dispersion in milliseconds */ long getRootDispersionInMillis(); /** * @return root dispersion in milliseconds */ double getRootDispersionInMillisDouble(); /** * @return stratum as defined in RFC-1305 */ int getStratum(); /** * @return the transmit timestamp as defined in RFC-1305 */ TimeStamp getTransmitTimeStamp(); /** * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...) correspond to the protocol used to obtain the timing information. * * @return packet type string identifier */ String getType(); /** * @return version as defined in RFC-1305 */ int getVersion(); /** * Set the contents of this object from the datagram packet * * @param dp the packet */ void setDatagramPacket(DatagramPacket dp); /** * Set leap indicator. * * @param li - leap indicator code */ void setLeapIndicator(int li); /** * Set mode as defined in RFC-1305 * * @param mode the mode to set */ void setMode(int mode); /** * Set originate timestamp given NTP TimeStamp object. * * @param ts - timestamp */ void setOriginateTimeStamp(TimeStamp ts); /** * Set poll interval as defined in RFC-1305. Field range between NTP_MINPOLL and NTP_MAXPOLL. * * @param poll the interval to set */ void setPoll(int poll); /** * Set precision as defined in RFC-1305 * * @param precision Precision * @since 3.4 */ void setPrecision(int precision); /** * Set receive timestamp given NTP TimeStamp object. * * @param ts - timestamp */ void setReceiveTimeStamp(TimeStamp ts); /** * Set reference clock identifier field. * * @param refId the clock id field to set */ void setReferenceId(int refId); /** * Set the reference timestamp given NTP TimeStamp object. * * @param ts - timestamp */ void setReferenceTime(TimeStamp ts); /** * Set root delay as defined in RFC-1305 * * @param delay the delay to set * @since 3.4 */ void setRootDelay(int delay); /** * * @param dispersion the value to set * @since 3.4 */ void setRootDispersion(int dispersion); /** * Set stratum as defined in RFC-1305 * * @param stratum the stratum to set */ void setStratum(int stratum); /** * Set the transmit timestamp given NTP TimeStamp object. * * @param ts - timestamp */ void setTransmitTime(TimeStamp ts); /** * Set version as defined in RFC-1305 * * @param version the version to set */ void setVersion(int version); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/TimeInfo.java000066400000000000000000000301451434047722200310720ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import java.net.DatagramPacket; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; /** * Wrapper class to network time packet messages (NTP, etc) that computes related timing info and stats. */ public class TimeInfo { private final NtpV3Packet message; private List comments; private Long delayMillis; private Long offsetMillis; /** * time at which time message packet was received by local machine */ private final long returnTimeMillis; /** * flag indicating that the TimeInfo details was processed and delay/offset were computed */ private boolean detailsComputed; /** * Create TimeInfo object with raw packet message and destination time received. * * @param message NTP message packet * @param returnTimeMillis destination receive time * @throws IllegalArgumentException if message is null */ public TimeInfo(final NtpV3Packet message, final long returnTimeMillis) { this(message, returnTimeMillis, null, true); } /** * Create TimeInfo object with raw packet message and destination time received. Auto-computes details if computeDetails flag set otherwise this is delayed * until computeDetails() is called. Delayed computation is for fast intialization when sub-millisecond timing is needed. * * @param msgPacket NTP message packet * @param returnTimeMillis destination receive time * @param doComputeDetails flag to pre-compute delay/offset values * @throws IllegalArgumentException if message is null */ public TimeInfo(final NtpV3Packet msgPacket, final long returnTimeMillis, final boolean doComputeDetails) { this(msgPacket, returnTimeMillis, null, doComputeDetails); } /** * Create TimeInfo object with raw packet message and destination time received. * * @param message NTP message packet * @param returnTimeMillis destination receive time * @param comments List of errors/warnings identified during processing * @throws IllegalArgumentException if message is null */ public TimeInfo(final NtpV3Packet message, final long returnTimeMillis, final List comments) { this(message, returnTimeMillis, comments, true); } /** * Create TimeInfo object with raw packet message and destination time received. Auto-computes details if computeDetails flag set otherwise this is delayed * until computeDetails() is called. Delayed computation is for fast intialization when sub-millisecond timing is needed. * * @param message NTP message packet * @param returnTimeMillis destination receive time * @param comments list of comments used to store errors/warnings with message * @param doComputeDetails flag to pre-compute delay/offset values * @throws IllegalArgumentException if message is null */ public TimeInfo(final NtpV3Packet message, final long returnTimeMillis, final List comments, final boolean doComputeDetails) { if (message == null) { throw new IllegalArgumentException("message cannot be null"); } this.returnTimeMillis = returnTimeMillis; this.message = message; this.comments = comments; if (doComputeDetails) { computeDetails(); } } /** * Add comment (error/warning) to list of comments associated with processing of NTP parameters. If comment list not create then one will be created. * * @param comment the comment */ public void addComment(final String comment) { if (comments == null) { comments = new ArrayList<>(); } comments.add(comment); } /** * Compute and validate details of the NTP message packet. Computed fields include the offset and delay. */ public void computeDetails() { if (detailsComputed) { return; // details already computed - do nothing } detailsComputed = true; if (comments == null) { comments = new ArrayList<>(); } final TimeStamp origNtpTime = message.getOriginateTimeStamp(); final long origTimeMillis = origNtpTime.getTime(); // Receive Time is time request received by server (t2) final TimeStamp rcvNtpTime = message.getReceiveTimeStamp(); final long rcvTimeMillis = rcvNtpTime.getTime(); // Transmit time is time reply sent by server (t3) final TimeStamp xmitNtpTime = message.getTransmitTimeStamp(); final long xmitTimeMillis = xmitNtpTime.getTime(); /* * Round-trip network delay and local clock offset (or time drift) is calculated according to this standard NTP equation: * * LocalClockOffset = ((ReceiveTimestamp - OriginateTimestamp) + (TransmitTimestamp - DestinationTimestamp)) / 2 * * equations from RFC-1305 (NTPv3) roundtrip delay = (t4 - t1) - (t3 - t2) local clock offset = ((t2 - t1) + (t3 - t4)) / 2 * * It takes into account network delays and assumes that they are symmetrical. * * Note the typo in SNTP RFCs 1769/2030 which state that the delay is (T4 - T1) - (T2 - T3) with the "T2" and "T3" switched. */ if (origNtpTime.ntpValue() == 0) { // without originate time cannot determine when packet went out // might be via a broadcast NTP packet... if (xmitNtpTime.ntpValue() != 0) { offsetMillis = Long.valueOf(xmitTimeMillis - returnTimeMillis); comments.add("Error: zero orig time -- cannot compute delay"); } else { comments.add("Error: zero orig time -- cannot compute delay/offset"); } } else if (rcvNtpTime.ntpValue() == 0 || xmitNtpTime.ntpValue() == 0) { comments.add("Warning: zero rcvNtpTime or xmitNtpTime"); // assert destTime >= origTime since network delay cannot be negative if (origTimeMillis > returnTimeMillis) { comments.add("Error: OrigTime > DestRcvTime"); } else { // without receive or xmit time cannot figure out processing time // so delay is simply the network travel time delayMillis = Long.valueOf(returnTimeMillis - origTimeMillis); } // TODO: is offset still valid if rcvNtpTime=0 || xmitNtpTime=0 ??? // Could always hash origNtpTime (sendTime) but if host doesn't set it // then it's an malformed ntp host anyway and we don't care? // If server is in broadcast mode then we never send out a query in first place... if (rcvNtpTime.ntpValue() != 0) { // xmitTime is 0 just use rcv time offsetMillis = Long.valueOf(rcvTimeMillis - origTimeMillis); } else if (xmitNtpTime.ntpValue() != 0) { // rcvTime is 0 just use xmitTime time offsetMillis = Long.valueOf(xmitTimeMillis - returnTimeMillis); } } else { long delayValueMillis = returnTimeMillis - origTimeMillis; // assert xmitTime >= rcvTime: difference typically < 1ms if (xmitTimeMillis < rcvTimeMillis) { // server cannot send out a packet before receiving it... comments.add("Error: xmitTime < rcvTime"); // time-travel not allowed } else { // subtract processing time from round-trip network delay final long deltaMillis = xmitTimeMillis - rcvTimeMillis; // in normal cases the processing delta is less than // the total roundtrip network travel time. if (deltaMillis <= delayValueMillis) { delayValueMillis -= deltaMillis; // delay = (t4 - t1) - (t3 - t2) } else // if delta - delayValue == 1 ms then it's a round-off error // e.g. delay=3ms, processing=4ms if (deltaMillis - delayValueMillis == 1) { // delayValue == 0 -> local clock saw no tick change but destination clock did if (delayValueMillis != 0) { comments.add("Info: processing time > total network time by 1 ms -> assume zero delay"); delayValueMillis = 0; } } else { comments.add("Warning: processing time > total network time"); } } delayMillis = Long.valueOf(delayValueMillis); if (origTimeMillis > returnTimeMillis) { comments.add("Error: OrigTime > DestRcvTime"); } offsetMillis = Long.valueOf(((rcvTimeMillis - origTimeMillis) + (xmitTimeMillis - returnTimeMillis)) / 2); } } /** * Compares this object against the specified object. The result is true if and only if the argument is not null and is a * TimeStamp object that contains the same values as this object. * * @param obj the object to compare with. * @return true if the objects are the same; false otherwise. * @since 3.4 */ @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final TimeInfo other = (TimeInfo) obj; return returnTimeMillis == other.returnTimeMillis && message.equals(other.message); } /** * Get host address from message datagram if available * * @return host address of available otherwise null * @since 3.4 */ public InetAddress getAddress() { final DatagramPacket pkt = message.getDatagramPacket(); return pkt == null ? null : pkt.getAddress(); } /** * Return list of comments (if any) during processing of NTP packet. * * @return List or null if not yet computed */ public List getComments() { return comments; } /** * Get round-trip network delay. If null then could not compute the delay. * * @return Long or null if delay not available. */ public Long getDelay() { return delayMillis; } /** * Returns NTP message packet. * * @return NTP message packet. */ public NtpV3Packet getMessage() { return message; } /** * Get clock offset needed to adjust local clock to match remote clock. If null then could not compute the offset. * * @return Long or null if offset not available. */ public Long getOffset() { return offsetMillis; } /** * Returns time at which time message packet was received by local machine. * * @return packet return time. */ public long getReturnTime() { return returnTimeMillis; } /** * Computes a hashcode for this object. The result is the exclusive OR of the return time and the message hash code. * * @return a hash code value for this object. * @since 3.4 */ @Override public int hashCode() { final int prime = 31; int result = (int) returnTimeMillis; result = prime * result + message.hashCode(); return result; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/TimeStamp.java000066400000000000000000000371241434047722200312670ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import java.io.Serializable; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; /** * TimeStamp class represents the Network Time Protocol (NTP) timestamp as defined in RFC-1305 and SNTP (RFC-2030). It is represented as a 64-bit unsigned * fixed-point number in seconds relative to 0-hour on 1-January-1900. The 32-bit low-order bits are the fractional seconds whose precision is about 200 * picoseconds. Assumes overflow date when date passes MAX_LONG and reverts back to 0 is 2036 and not 1900. Test for most significant bit: if MSB=0 then 2036 * basis is used otherwise 1900 if MSB=1. *

* Methods exist to convert NTP timestamps to and from the equivalent Java date representation, which is the number of milliseconds since the standard base time * known as "the epoch", namely January 1, 1970, 00:00:00 GMT. *

* * @see java.util.Date */ public class TimeStamp implements Serializable, Comparable { private static final long serialVersionUID = 8139806907588338737L; /** * Baseline NTP time if bit-0=0 is 7-Feb-2036 @ 06:28:16 UTC */ protected static final long msb0baseTime = 2085978496000L; /** * Baseline NTP time if bit-0=1 is 1-Jan-1900 @ 01:00:00 UTC */ protected static final long msb1baseTime = -2208988800000L; /** * Default NTP date string format. E.g. Fri, Sep 12 2003 21:06:23.860. See java.text.SimpleDateFormat for code descriptions. */ public static final String NTP_DATE_FORMAT = "EEE, MMM dd yyyy HH:mm:ss.SSS"; /** * Left-pad 8-character hex string with 0's * * @param buf - StringBuilder which is appended with leading 0's. * @param l - a long. */ private static void appendHexString(final StringBuilder buf, final long l) { final String s = Long.toHexString(l); for (int i = s.length(); i < 8; i++) { buf.append('0'); } buf.append(s); } /** * Convert NTP timestamp hexstring (e.g. "c1a089bd.fc904f6d") to the NTP 64-bit unsigned fixed-point number. * * @param hexString the string to convert * * @return NTP 64-bit timestamp value. * @throws NumberFormatException - if the string does not contain a parsable timestamp. */ protected static long decodeNtpHexString(final String hexString) throws NumberFormatException { if (hexString == null) { throw new NumberFormatException("null"); } final int ind = hexString.indexOf('.'); if (ind == -1) { if (hexString.isEmpty()) { return 0; } return Long.parseLong(hexString, 16) << 32; // no decimal } return Long.parseLong(hexString.substring(0, ind), 16) << 32 | Long.parseLong(hexString.substring(ind + 1), 16); } /** * Constructs a NTP timestamp object and initializes it so that it represents the time at which it was allocated, measured to the nearest millisecond. * * @return NTP timestamp object set to the current time. * @see System#currentTimeMillis() */ public static TimeStamp getCurrentTime() { return getNtpTime(System.currentTimeMillis()); } // initialization of static time bases /* * static { TimeZone utcZone = TimeZone.getTimeZone("UTC"); Calendar calendar = Calendar.getInstance(utcZone); calendar.set(1900, Calendar.JANUARY, 1, 0, 0, * 0); calendar.set(Calendar.MILLISECOND, 0); msb1baseTime = calendar.getTime().getTime(); calendar.set(2036, Calendar.FEBRUARY, 7, 6, 28, 16); * calendar.set(Calendar.MILLISECOND, 0); msb0baseTime = calendar.getTime().getTime(); } */ /** * Helper method to convert Java time to NTP timestamp object. Note that Java time (milliseconds) by definition has less precision then NTP time * (picoseconds) so converting Ntptime to Javatime and back to Ntptime loses precision. For example, Tue, Dec 17 2002 09:07:24.810 is represented by a * single Java-based time value of f22cd1fc8a, but its NTP equivalent are all values from c1a9ae1c.cf5c28f5 to c1a9ae1c.cf9db22c. * * @param dateMillis the milliseconds since January 1, 1970, 00:00:00 GMT. * @return NTP timestamp object at the specified date. */ public static TimeStamp getNtpTime(final long dateMillis) { return new TimeStamp(toNtpTime(dateMillis)); } /** * Converts 64-bit NTP timestamp to Java standard time. * * Note that java time (milliseconds) by definition has less precision then NTP time (picoseconds) so converting NTP timestamp to java time and back to NTP * timestamp loses precision. For example, Tue, Dec 17 2002 09:07:24.810 EST is represented by a single Java-based time value of f22cd1fc8a, but its NTP * equivalent are all values ranging from c1a9ae1c.cf5c28f5 to c1a9ae1c.cf9db22c. * * @param ntpTimeValue the input time * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this NTP timestamp value. */ public static long getTime(final long ntpTimeValue) { final long seconds = (ntpTimeValue >>> 32) & 0xffffffffL; // high-order 32-bits long fraction = ntpTimeValue & 0xffffffffL; // low-order 32-bits // Use round-off on fractional part to preserve going to lower precision fraction = Math.round(1000D * fraction / 0x100000000L); /* * If the most significant bit (MSB) on the seconds field is set we use a different time base. The following text is a quote from RFC-2030 (SNTP v4): * * If bit 0 is set, the UTC time is in the range 1968-2036 and UTC time is reckoned from 0h 0m 0s UTC on 1 January 1900. If bit 0 is not set, the time * is in the range 2036-2104 and UTC time is reckoned from 6h 28m 16s UTC on 7 February 2036. */ final long msb = seconds & 0x80000000L; if (msb == 0) { // use base: 7-Feb-2036 @ 06:28:16 UTC return msb0baseTime + (seconds * 1000) + fraction; } // use base: 1-Jan-1900 @ 01:00:00 UTC return msb1baseTime + (seconds * 1000) + fraction; } /** * Parses the string argument as a NTP hexidecimal timestamp representation string (e.g. "c1a089bd.fc904f6d"). * * @param s - hexstring. * @return the Timestamp represented by the argument in hexidecimal. * @throws NumberFormatException - if the string does not contain a parsable timestamp. */ public static TimeStamp parseNtpString(final String s) throws NumberFormatException { return new TimeStamp(decodeNtpHexString(s)); } /** * Converts Java time to 64-bit NTP time representation. * * @param millis Java time * @return NTP timestamp representation of Java time value. */ protected static long toNtpTime(final long millis) { final boolean useBase1 = millis < msb0baseTime; // time < Feb-2036 final long baseTimeMillis; if (useBase1) { baseTimeMillis = millis - msb1baseTime; // dates <= Feb-2036 } else { // if base0 needed for dates >= Feb-2036 baseTimeMillis = millis - msb0baseTime; } long seconds = baseTimeMillis / 1000; final long fraction = ((baseTimeMillis % 1000) * 0x100000000L) / 1000; if (useBase1) { seconds |= 0x80000000L; // set high-order bit if msb1baseTime 1900 used } return seconds << 32 | fraction; } /** * Converts 64-bit NTP timestamp value to a String. The NTP timestamp value is represented as hex string with seconds separated by fractional * seconds by a decimal point; e.g. c1a089bd.fc904f6d == Tue, Dec 10 2002 10:41:49.986 * * @param ntpTime the 64 bit timestamp * * @return NTP timestamp 64-bit long value as hex string with seconds separated by fractional seconds. */ public static String toString(final long ntpTime) { final StringBuilder buf = new StringBuilder(); // high-order second bits (32..63) as hexstring appendHexString(buf, (ntpTime >>> 32) & 0xffffffffL); // low-order fractional seconds bits (0..31) as hexstring buf.append('.'); appendHexString(buf, ntpTime & 0xffffffffL); return buf.toString(); } /** * NTP timestamp value: 64-bit unsigned fixed-point number as defined in RFC-1305 with high-order 32 bits the seconds field and the low-order 32-bits the * fractional field. */ private final long ntpTime; private DateFormat simpleFormatter; private DateFormat utcFormatter; /** * Constructs a newly allocated NTP timestamp object that represents the Java Date argument. * * @param d - the Date to be represented by the Timestamp object. */ public TimeStamp(final Date d) { ntpTime = d == null ? 0 : toNtpTime(d.getTime()); } /** * Constructs a newly allocated NTP timestamp object that represents the native 64-bit long argument. * * @param ntpTime the timestamp */ public TimeStamp(final long ntpTime) { this.ntpTime = ntpTime; } /** * Constructs a newly allocated NTP timestamp object that represents the value represented by the string in hexdecimal form (e.g. "c1a089bd.fc904f6d"). * * @param hexStamp the hex timestamp * * @throws NumberFormatException - if the string does not contain a parsable timestamp. */ public TimeStamp(final String hexStamp) throws NumberFormatException { ntpTime = decodeNtpHexString(hexStamp); } /** * Compares two Timestamps numerically. * * @param anotherTimeStamp - the TimeStamp to be compared. * @return the value 0 if the argument TimeStamp is equal to this TimeStamp; a value less than 0 if this TimeStamp is numerically * less than the TimeStamp argument; and a value greater than 0 if this TimeStamp is numerically greater than the TimeStamp argument * (signed comparison). */ @Override public int compareTo(final TimeStamp anotherTimeStamp) { final long thisVal = this.ntpTime; final long anotherVal = anotherTimeStamp.ntpTime; return (Long.compare(thisVal, anotherVal)); } /** * Compares this object against the specified object. The result is true if and only if the argument is not null and is a * Long object that contains the same long value as this object. * * @param obj the object to compare with. * @return true if the objects are the same; false otherwise. */ @Override public boolean equals(final Object obj) { if (obj instanceof TimeStamp) { return ntpTime == ((TimeStamp) obj).ntpValue(); } return false; } /** * Converts NTP timestamp to Java Date object. * * @return NTP Timestamp in Java Date */ public Date getDate() { return new Date(getTime(ntpTime)); } /** * Returns low-order 32-bits representing the fractional seconds. * * @return fractional seconds represented by this NTP timestamp. */ public long getFraction() { return ntpTime & 0xffffffffL; } /** * Returns high-order 32-bits representing the seconds of this NTP timestamp. * * @return seconds represented by this NTP timestamp. */ public long getSeconds() { return (ntpTime >>> 32) & 0xffffffffL; } /** * Converts NTP timestamp to Java standard time. * * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this NTP timestamp value. */ public long getTime() { return getTime(ntpTime); } /** * Computes a hashcode for this Timestamp. The result is the exclusive OR of the two halves of the primitive long value represented by this * TimeStamp object. That is, the hashcode is the value of the expression:
* *
     * {@code
     * (int) (this.ntpValue() ^ (this.ntpValue() >>> 32))
     * }
     * 
* *
* * @return a hash code value for this object. */ @Override public int hashCode() { return (int) (ntpTime ^ (ntpTime >>> 32)); } /** * Returns the value of this Timestamp as a long value. * * @return the 64-bit long value represented by this object. */ public long ntpValue() { return ntpTime; } private void readObject(final java.io.ObjectInputStream in) { throw new UnsupportedOperationException("Serialization is not supported"); } /** * Converts this TimeStamp object to a String of the form:
* *
     * EEE, MMM dd yyyy HH:mm:ss.SSS
     * 
* *
See java.text.SimpleDataFormat for code descriptions. * * @return a string representation of this date. */ public String toDateString() { if (simpleFormatter == null) { simpleFormatter = new SimpleDateFormat(NTP_DATE_FORMAT, Locale.US); simpleFormatter.setTimeZone(TimeZone.getDefault()); } final Date ntpDate = getDate(); return simpleFormatter.format(ntpDate); } /** * Converts this TimeStamp object to a String. The NTP timestamp 64-bit long value is represented as hex string with seconds * separated by fractional seconds by a decimal point; e.g. c1a089bd.fc904f6d == Tue, Dec 10 2002 10:41:49.986 * * @return NTP timestamp 64-bit long value as hex string with seconds separated by fractional seconds. */ @Override public String toString() { return toString(ntpTime); } /* * Serialization is unnecessary for this class. Reject attempts to do so until such time as the Serializable attribute can be dropped. */ /** * Converts this TimeStamp object to a String of the form:
* *
     * EEE, MMM dd yyyy HH:mm:ss.SSS UTC
     * 
* *
See java.text.SimpleDataFormat for code descriptions. * * @return a string representation of this date in UTC. */ public String toUTCString() { if (utcFormatter == null) { utcFormatter = new SimpleDateFormat(NTP_DATE_FORMAT + " 'UTC'", Locale.US); utcFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); } final Date ntpDate = getDate(); return utcFormatter.format(ntpDate); } private void writeObject(final java.io.ObjectOutputStream out) { throw new UnsupportedOperationException("Serialization is not supported"); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/ntp/package-info.java000066400000000000000000000015561434047722200317100ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * NTP - network time protocol */ package org.apache.commons.net.ntp;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/package-info.java000066400000000000000000000016111434047722200310770ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Common socket classes and protocol command utility classes */ package org.apache.commons.net;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/000077500000000000000000000000001434047722200265725ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/ExtendedPOP3Client.java000066400000000000000000000131311434047722200327750ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.net.util.Base64; /** * A POP3 Cilent class with protocol and authentication extensions support (RFC2449 and RFC2195). * * @see POP3Client * @since 3.0 */ public class ExtendedPOP3Client extends POP3SClient { /** * The enumeration of currently-supported authentication methods. */ public enum AUTH_METHOD { /** The standarised (RFC4616) PLAIN method, which sends the password unencrypted (insecure). */ PLAIN("PLAIN"), /** The standarised (RFC2195) CRAM-MD5 method, which doesn't send the password (secure). */ CRAM_MD5("CRAM-MD5"); private final String methodName; AUTH_METHOD(final String methodName) { this.methodName = methodName; } /** * Gets the name of the given authentication method suitable for the server. * * @return The name of the given authentication method suitable for the server. */ public final String getAuthName() { return this.methodName; } } /** * The default ExtendedPOP3Client constructor. Creates a new Extended POP3 Client. * * @throws NoSuchAlgorithmException on error */ public ExtendedPOP3Client() throws NoSuchAlgorithmException { } /** * Authenticate to the POP3 server by sending the AUTH command with the selected mechanism, using the given username and the given password. *

* * @param method the {@link AUTH_METHOD} to use * @param username the user name * @param password the password * @return True if successfully completed, false if not. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @throws NoSuchAlgorithmException If the CRAM hash algorithm cannot be instantiated by the Java runtime system. * @throws InvalidKeyException If the CRAM hash algorithm failed to use the given password. * @throws InvalidKeySpecException If the CRAM hash algorithm failed to use the given password. */ public boolean auth(final AUTH_METHOD method, final String username, final String password) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException { if (sendCommand(POP3Command.AUTH, method.getAuthName()) != POP3Reply.OK_INT) { return false; } switch (method) { case PLAIN: // the server sends an empty response ("+ "), so we don't have to read it. return sendCommand(new String(Base64.encodeBase64(("\000" + username + "\000" + password).getBytes(getCharset())), getCharset())) == POP3Reply.OK; case CRAM_MD5: // get the CRAM challenge final byte[] serverChallenge = Base64.decodeBase64(getReplyString().substring(2).trim()); // get the Mac instance final Mac hmac_md5 = Mac.getInstance("HmacMD5"); hmac_md5.init(new SecretKeySpec(password.getBytes(getCharset()), "HmacMD5")); // compute the result: final byte[] hmacResult = convertToHexString(hmac_md5.doFinal(serverChallenge)).getBytes(getCharset()); // join the byte arrays to form the reply final byte[] usernameBytes = username.getBytes(getCharset()); final byte[] toEncode = new byte[usernameBytes.length + 1 /* the space */ + hmacResult.length]; System.arraycopy(usernameBytes, 0, toEncode, 0, usernameBytes.length); toEncode[usernameBytes.length] = ' '; System.arraycopy(hmacResult, 0, toEncode, usernameBytes.length + 1, hmacResult.length); // send the reply and read the server code: return sendCommand(Base64.encodeBase64StringUnChunked(toEncode)) == POP3Reply.OK; default: return false; } } /** * Converts the given byte array to a String containing the hex values of the bytes. For example, the byte 'A' will be converted to '41', because this is * the ASCII code (and the byte value) of the capital letter 'A'. * * @param a The byte array to convert. * @return The resulting String of hex codes. */ private String convertToHexString(final byte[] a) { final StringBuilder result = new StringBuilder(a.length * 2); for (final byte element : a) { if ((element & 0x0FF) <= 15) { result.append("0"); } result.append(Integer.toHexString(element & 0x0FF)); } return result.toString(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/POP3.java000066400000000000000000000261661434047722200301710ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.apache.commons.net.MalformedServerReplyException; import org.apache.commons.net.ProtocolCommandSupport; import org.apache.commons.net.SocketClient; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.util.NetConstants; /** * The POP3 class is not meant to be used by itself and is provided only so that you may easily implement your own POP3 client if you so desire. If you have no * need to perform your own implementation, you should use {@link org.apache.commons.net.pop3.POP3Client}. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. * * * @see POP3Client * @see org.apache.commons.net.MalformedServerReplyException */ public class POP3 extends SocketClient { /** The default POP3 port. Set to 110 according to RFC 1288. */ public static final int DEFAULT_PORT = 110; /** * A constant representing the state where the client is not yet connected to a POP3 server. */ public static final int DISCONNECTED_STATE = -1; /** A constant representing the POP3 authorization state. */ public static final int AUTHORIZATION_STATE = 0; /** A constant representing the POP3 transaction state. */ public static final int TRANSACTION_STATE = 1; /** A constant representing the POP3 update state. */ public static final int UPDATE_STATE = 2; static final String OK = "+OK"; // The reply indicating intermediate response to a command. static final String OK_INT = "+ "; static final String ERROR = "-ERR"; // We have to ensure that the protocol communication is in ASCII // but we use ISO-8859-1 just in case 8-bit characters cross // the wire. static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1; private int popState; BufferedWriter writer; BufferedReader reader; int replyCode; String lastReplyLine; List replyLines; /** * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and the firing of ProtocolCommandEvents. */ protected ProtocolCommandSupport _commandSupport_; /** * The default POP3Client constructor. Initializes the state to DISCONNECTED_STATE. */ public POP3() { setDefaultPort(DEFAULT_PORT); popState = DISCONNECTED_STATE; reader = null; writer = null; replyLines = new ArrayList<>(); _commandSupport_ = new ProtocolCommandSupport(this); } /** * Performs connection initialization and sets state to AUTHORIZATION_STATE . */ @Override protected void _connectAction_() throws IOException { super._connectAction_(); reader = new CRLFLineReader(new InputStreamReader(_input_, DEFAULT_ENCODING)); writer = new BufferedWriter(new OutputStreamWriter(_output_, DEFAULT_ENCODING)); getReply(); setState(AUTHORIZATION_STATE); } /** * Disconnects the client from the server, and sets the state to DISCONNECTED_STATE . The reply text information from the last issued command * is voided to allow garbage collection of the memory used to store that information. * * @throws IOException If there is an error in disconnecting. */ @Override public void disconnect() throws IOException { super.disconnect(); reader = null; writer = null; lastReplyLine = null; replyLines.clear(); setState(DISCONNECTED_STATE); } /** * Retrieves the additional lines of a multi-line server reply. * * @throws IOException on error */ public void getAdditionalReply() throws IOException { String line; line = reader.readLine(); while (line != null) { replyLines.add(line); if (line.equals(".")) { break; } line = reader.readLine(); } } /** * Provide command support to super-class */ @Override protected ProtocolCommandSupport getCommandSupport() { return _commandSupport_; } private void getReply() throws IOException { final String line; replyLines.clear(); line = reader.readLine(); if (line == null) { throw new EOFException("Connection closed without indication."); } if (line.startsWith(OK)) { replyCode = POP3Reply.OK; } else if (line.startsWith(ERROR)) { replyCode = POP3Reply.ERROR; } else if (line.startsWith(OK_INT)) { replyCode = POP3Reply.OK_INT; } else { throw new MalformedServerReplyException("Received invalid POP3 protocol response from server." + line); } replyLines.add(line); lastReplyLine = line; fireReplyReceived(replyCode, getReplyString()); } /** * Returns the reply to the last command sent to the server. The value is a single string containing all the reply lines including newlines. If the reply is * a single line, but its format ndicates it should be a multiline reply, then you must call {@link #getAdditionalReply getAdditionalReply() } to fetch the * rest of the reply, and then call getReplyString again. You only have to worry about this if you are implementing your own client using the * {@link #sendCommand sendCommand } methods. * * @return The last server response. */ public String getReplyString() { final StringBuilder buffer = new StringBuilder(256); for (final String entry : replyLines) { buffer.append(entry); buffer.append(SocketClient.NETASCII_EOL); } return buffer.toString(); } /** * Returns an array of lines received as a reply to the last command sent to the server. The lines have end of lines truncated. If the reply is a single * line, but its format ndicates it should be a multiline reply, then you must call {@link #getAdditionalReply getAdditionalReply() } to fetch the rest of * the reply, and then call getReplyStrings again. You only have to worry about this if you are implementing your own client using the * {@link #sendCommand sendCommand } methods. * * @return The last server response. */ public String[] getReplyStrings() { return replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY); } /** * Returns the current POP3 client state. * * @return The current POP3 client state. */ public int getState() { return popState; } /** * Removes a ProtocolCommandListener. * * Delegates this incorrectly named method - removeProtocolCommandistener (note the missing "L")- to the correct method * {@link SocketClient#removeProtocolCommandListener} * * @param listener The ProtocolCommandListener to remove */ public void removeProtocolCommandistener(final org.apache.commons.net.ProtocolCommandListener listener) { removeProtocolCommandListener(listener); } /** * Sends a command with no arguments to the server and returns the reply code. * * @param command The POP3 command to send (one of the POP3Command constants). * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). * @throws IOException on error */ public int sendCommand(final int command) throws IOException { return sendCommand(POP3Command.commands[command], null); } /** * Sends a command an arguments to the server and returns the reply code. * * @param command The POP3 command to send (one of the POP3Command constants). * @param args The command arguments. * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). * @throws IOException on error */ public int sendCommand(final int command, final String args) throws IOException { return sendCommand(POP3Command.commands[command], args); } /** * Sends a command with no arguments to the server and returns the reply code. * * @param command The POP3 command to send. * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). * @throws IOException on error */ public int sendCommand(final String command) throws IOException { return sendCommand(command, null); } /** * Sends a command an arguments to the server and returns the reply code. * * @param command The POP3 command to send. * @param args The command arguments. * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). * @throws IOException on error */ public int sendCommand(final String command, final String args) throws IOException { if (writer == null) { throw new IllegalStateException("Socket is not connected"); } final StringBuilder __commandBuffer = new StringBuilder(); __commandBuffer.append(command); if (args != null) { __commandBuffer.append(' '); __commandBuffer.append(args); } __commandBuffer.append(SocketClient.NETASCII_EOL); final String message = __commandBuffer.toString(); writer.write(message); writer.flush(); fireCommandSent(command, message); getReply(); return replyCode; } /** * Set the internal POP3 state. * * @param state the new state. This must be one of the _STATE constants. */ public void setState(final int state) { popState = state; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/POP3Client.java000066400000000000000000000511101434047722200313130ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.IOException; import java.io.Reader; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.ListIterator; import java.util.StringTokenizer; import org.apache.commons.net.io.DotTerminatedMessageReader; /** * The POP3Client class implements the client side of the Internet POP3 Protocol defined in RFC 1939. All commands are supported, including the APOP command * which requires MD5 encryption. See RFC 1939 for more details on the POP3 protocol. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. * * * @see POP3MessageInfo * @see org.apache.commons.net.io.DotTerminatedMessageReader * @see org.apache.commons.net.MalformedServerReplyException */ public class POP3Client extends POP3 { private static POP3MessageInfo parseStatus(final String line) { int num, size; final StringTokenizer tokenizer; tokenizer = new StringTokenizer(line); if (!tokenizer.hasMoreElements()) { return null; } num = size = 0; try { num = Integer.parseInt(tokenizer.nextToken()); if (!tokenizer.hasMoreElements()) { return null; } size = Integer.parseInt(tokenizer.nextToken()); } catch (final NumberFormatException e) { return null; } return new POP3MessageInfo(num, size); } private static POP3MessageInfo parseUID(String line) { int num; final StringTokenizer tokenizer; tokenizer = new StringTokenizer(line); if (!tokenizer.hasMoreElements()) { return null; } num = 0; try { num = Integer.parseInt(tokenizer.nextToken()); if (!tokenizer.hasMoreElements()) { return null; } line = tokenizer.nextToken(); } catch (final NumberFormatException e) { return null; } return new POP3MessageInfo(num, line); } /** * Send a CAPA command to the POP3 server. * * @return True if the command was successful, false if not. * @throws IOException If a network I/O error occurs in the process of sending the CAPA command. * @since 3.1 (was previously in ExtendedPOP3Client) */ public boolean capa() throws IOException { if (sendCommand(POP3Command.CAPA) == POP3Reply.OK) { getAdditionalReply(); return true; } return false; } /** * Delete a message from the POP3 server. The message is only marked for deletion by the server. If you decide to unmark the message, you must issuse a * {@link #reset reset } command. Messages marked for deletion are only deleted by the server on {@link #logout logout }. A delete attempt can only succeed * if the client is in the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . * * @param messageId The message number to delete. * @return True if the deletion attempt was successful, false if not. * @throws IOException If a network I/O error occurs in the process of sending the delete command. */ public boolean deleteMessage(final int messageId) throws IOException { if (getState() == TRANSACTION_STATE) { return sendCommand(POP3Command.DELE, Integer.toString(messageId)) == POP3Reply.OK; } return false; } /** * List an individual message. A list attempt can only succeed if the client is in the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE * TRANSACTION_STATE } . Returns a POP3MessageInfo instance containing the number of the listed message and the size of the message in bytes. Returns null * if the list attempt fails (e.g., if the specified message number does not exist). * * @param messageId The number of the message list. * @return A POP3MessageInfo instance containing the number of the listed message and the size of the message in bytes. Returns null if the list attempt * fails. * @throws IOException If a network I/O error occurs in the process of sending the list command. */ public POP3MessageInfo listMessage(final int messageId) throws IOException { if (getState() != TRANSACTION_STATE) { return null; } if (sendCommand(POP3Command.LIST, Integer.toString(messageId)) != POP3Reply.OK) { return null; } return parseStatus(lastReplyLine.substring(3)); } /** * List all messages. A list attempt can only succeed if the client is in the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } * . Returns an array of POP3MessageInfo instances, each containing the number of a message and its size in bytes. If there are no messages, this method * returns a zero length array. If the list attempt fails, it returns null. * * @return An array of POP3MessageInfo instances representing all messages in the order they appear in the mailbox, each containing the number of a message * and its size in bytes. If there are no messages, this method returns a zero length array. If the list attempt fails, it returns null. * @throws IOException If a network I/O error occurs in the process of sending the list command. */ public POP3MessageInfo[] listMessages() throws IOException { if (getState() != TRANSACTION_STATE) { return null; } if (sendCommand(POP3Command.LIST) != POP3Reply.OK) { return null; } getAdditionalReply(); // This could be a zero length array if no messages present final POP3MessageInfo[] messages = new POP3MessageInfo[replyLines.size() - 2]; // skip first and last lines final ListIterator en = replyLines.listIterator(1); // Skip first line // Fetch lines. Arrays.setAll(messages, i -> parseStatus(en.next())); return messages; } /** * List the unique identifier for a message. A list attempt can only succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . Returns a POP3MessageInfo instance containing the number of the listed * message and the unique identifier for that message. Returns null if the list attempt fails (e.g., if the specified message number does not exist). * * @param messageId The number of the message list. * @return A POP3MessageInfo instance containing the number of the listed message and the unique identifier for that message. Returns null if the list * attempt fails. * @throws IOException If a network I/O error occurs in the process of sending the list unique identifier command. */ public POP3MessageInfo listUniqueIdentifier(final int messageId) throws IOException { if (getState() != TRANSACTION_STATE) { return null; } if (sendCommand(POP3Command.UIDL, Integer.toString(messageId)) != POP3Reply.OK) { return null; } return parseUID(lastReplyLine.substring(3)); } /** * List the unique identifiers for all messages. A list attempt can only succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . Returns an array of POP3MessageInfo instances, each containing the number * of a message and its unique identifier. If there are no messages, this method returns a zero length array. If the list attempt fails, it returns null. * * @return An array of POP3MessageInfo instances representing all messages in the order they appear in the mailbox, each containing the number of a message * and its unique identifier If there are no messages, this method returns a zero length array. If the list attempt fails, it returns null. * @throws IOException If a network I/O error occurs in the process of sending the list unique identifier command. */ public POP3MessageInfo[] listUniqueIdentifiers() throws IOException { if (getState() != TRANSACTION_STATE) { return null; } if (sendCommand(POP3Command.UIDL) != POP3Reply.OK) { return null; } getAdditionalReply(); // This could be a zero length array if no messages present final POP3MessageInfo[] messages = new POP3MessageInfo[replyLines.size() - 2]; // skip first and last lines final ListIterator en = replyLines.listIterator(1); // skip first line // Fetch lines. Arrays.setAll(messages, i -> parseUID(en.next())); return messages; } /** * Login to the POP3 server with the given username and password. You must first connect to the server with * {@link org.apache.commons.net.SocketClient#connect connect } before attempting to login. A login attempt is only valid if the client is in the * {@link org.apache.commons.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE } . After logging in, the client enters the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . * * @param username The account name being logged in to. * @param password The plain text password of the account. * @return True if the login attempt was successful, false if not. * @throws IOException If a network I/O error occurs in the process of logging in. */ public boolean login(final String username, final String password) throws IOException { if (getState() != AUTHORIZATION_STATE) { return false; } if (sendCommand(POP3Command.USER, username) != POP3Reply.OK) { return false; } if (sendCommand(POP3Command.PASS, password) != POP3Reply.OK) { return false; } setState(TRANSACTION_STATE); return true; } /** * Login to the POP3 server with the given username and authentication information. Use this method when connecting to a server requiring authentication * using the APOP command. Because the timestamp produced in the greeting banner varies from server to server, it is not possible to consistently extract * the information. Therefore, after connecting to the server, you must call {@link org.apache.commons.net.pop3.POP3#getReplyString getReplyString } and * parse out the timestamp information yourself. *

* You must first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before attempting to login. A login attempt is * only valid if the client is in the {@link org.apache.commons.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE } . After logging in, the client * enters the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . After connecting, you must parse out the server specific * information to use as a timestamp, and pass that information to this method. The secret is a shared secret known to you and the server. See RFC 1939 for * more details regarding the APOP command. * * @param username The account name being logged in to. * @param timestamp The timestamp string to combine with the secret. * @param secret The shared secret which produces the MD5 digest when combined with the timestamp. * @return True if the login attempt was successful, false if not. * @throws IOException If a network I/O error occurs in the process of logging in. * @throws NoSuchAlgorithmException If the MD5 encryption algorithm cannot be instantiated by the Java runtime system. */ public boolean login(final String username, String timestamp, final String secret) throws IOException, NoSuchAlgorithmException { int i; final byte[] digest; final StringBuilder buffer; final StringBuilder digestBuffer; final MessageDigest md5; if (getState() != AUTHORIZATION_STATE) { return false; } md5 = MessageDigest.getInstance("MD5"); timestamp += secret; digest = md5.digest(timestamp.getBytes(getCharset())); digestBuffer = new StringBuilder(128); for (i = 0; i < digest.length; i++) { final int digit = digest[i] & 0xff; if (digit <= 15) { // Add leading zero if necessary (NET-351) digestBuffer.append("0"); } digestBuffer.append(Integer.toHexString(digit)); } buffer = new StringBuilder(256); buffer.append(username); buffer.append(' '); buffer.append(digestBuffer.toString()); if (sendCommand(POP3Command.APOP, buffer.toString()) != POP3Reply.OK) { return false; } setState(TRANSACTION_STATE); return true; } /** * Logout of the POP3 server. To fully disconnect from the server you must call {@link org.apache.commons.net.pop3.POP3#disconnect disconnect }. A logout * attempt is valid in any state. If the client is in the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } , it enters the * {@link org.apache.commons.net.pop3.POP3#UPDATE_STATE UPDATE_STATE } on a successful logout. * * @return True if the logout attempt was successful, false if not. * @throws IOException If a network I/O error occurs in the process of logging out. */ public boolean logout() throws IOException { if (getState() == TRANSACTION_STATE) { setState(UPDATE_STATE); } sendCommand(POP3Command.QUIT); return replyCode == POP3Reply.OK; } /** * Send a NOOP command to the POP3 server. This is useful for keeping a connection alive since most POP3 servers will timeout after 10 minutes of * inactivity. A noop attempt will only succeed if the client is in the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . * * @return True if the noop attempt was successful, false if not. * @throws IOException If a network I/O error occurs in the process of sending the NOOP command. */ public boolean noop() throws IOException { if (getState() == TRANSACTION_STATE) { return sendCommand(POP3Command.NOOP) == POP3Reply.OK; } return false; } /** * Reset the POP3 session. This is useful for undoing any message deletions that may have been performed. A reset attempt can only succeed if the client is * in the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . * * @return True if the reset attempt was successful, false if not. * @throws IOException If a network I/O error occurs in the process of sending the reset command. */ public boolean reset() throws IOException { if (getState() == TRANSACTION_STATE) { return sendCommand(POP3Command.RSET) == POP3Reply.OK; } return false; } /** * Retrieve a message from the POP3 server. A retrieve message attempt can only succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } *

* You must not issue any commands to the POP3 server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The POP3 protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the POP3 connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. * * @param messageId The number of the message to fetch. * @return A DotTerminatedMessageReader instance from which the entire message can be read. This can safely be cast to a {@link java.io.BufferedReader * BufferedReader} in order to use the {@link java.io.BufferedReader#readLine() BufferedReader#readLine()} method. Returns null if the retrieval * attempt fails (e.g., if the specified message number does not exist). * @throws IOException If a network I/O error occurs in the process of sending the retrieve message command. */ public Reader retrieveMessage(final int messageId) throws IOException { if (getState() != TRANSACTION_STATE) { return null; } if (sendCommand(POP3Command.RETR, Integer.toString(messageId)) != POP3Reply.OK) { return null; } return new DotTerminatedMessageReader(reader); } /** * Retrieve only the specified top number of lines of a message from the POP3 server. A retrieve top lines attempt can only succeed if the client is in the * {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } *

* You must not issue any commands to the POP3 server (i.e., call any other methods) until you finish reading the message from the returned BufferedReader * instance. The POP3 protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned BufferedReader actually * reads directly from the POP3 connection. After the end of message has been reached, new commands can be executed and their replies read. If you do not * follow these requirements, your program will not work properly. * * @param messageId The number of the message to fetch. * @param numLines The top number of lines to fetch. This must be >= 0. * @return A DotTerminatedMessageReader instance from which the specified top number of lines of the message can be read. This can safely be cast to a * {@link java.io.BufferedReader BufferedReader} in order to use the {@link java.io.BufferedReader#readLine() BufferedReader#readLine()} method. * Returns null if the retrieval attempt fails (e.g., if the specified message number does not exist). * @throws IOException If a network I/O error occurs in the process of sending the top command. */ public Reader retrieveMessageTop(final int messageId, final int numLines) throws IOException { if (numLines < 0 || getState() != TRANSACTION_STATE) { return null; } if (sendCommand(POP3Command.TOP, Integer.toString(messageId) + " " + Integer.toString(numLines)) != POP3Reply.OK) { return null; } return new DotTerminatedMessageReader(reader); } /** * Get the mailbox status. A status attempt can only succeed if the client is in the {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE * TRANSACTION_STATE } . Returns a POP3MessageInfo instance containing the number of messages in the mailbox and the total size of the messages in bytes. * Returns null if the status the attempt fails. * * @return A POP3MessageInfo instance containing the number of messages in the mailbox and the total size of the messages in bytes. Returns null if the * status the attempt fails. * @throws IOException If a network I/O error occurs in the process of sending the status command. */ public POP3MessageInfo status() throws IOException { if (getState() != TRANSACTION_STATE) { return null; } if (sendCommand(POP3Command.STAT) != POP3Reply.OK) { return null; } return parseStatus(lastReplyLine.substring(3)); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/POP3Command.java000066400000000000000000000053421434047722200314610ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; /** * POP3Command stores POP3 command code constants. * * */ public final class POP3Command { /** Send user name. */ public static final int USER = 0; /** Send password. */ public static final int PASS = 1; /** Quit session. */ public static final int QUIT = 2; /** Get status. */ public static final int STAT = 3; /** List message(s). */ public static final int LIST = 4; /** Retrieve message(s). */ public static final int RETR = 5; /** Delete message(s). */ public static final int DELE = 6; /** No operation. Used as a session keepalive. */ public static final int NOOP = 7; /** Reset session. */ public static final int RSET = 8; /** Authorization. */ public static final int APOP = 9; /** Retrieve top number lines from message. */ public static final int TOP = 10; /** List unique message identifier(s). */ public static final int UIDL = 11; /** * The capabilities command. * * @since 3.0 */ public static final int CAPA = 12; /** * Authentication * * @since 3.0 */ public static final int AUTH = 13; private static final int NEXT = AUTH + 1; // update as necessary when adding new entries static final String[] commands = { "USER", "PASS", "QUIT", "STAT", "LIST", "RETR", "DELE", "NOOP", "RSET", "APOP", "TOP", "UIDL", "CAPA", "AUTH", }; static { if (commands.length != NEXT) { throw new RuntimeException("Error in array definition"); } } /** * Get the POP3 protocol string command corresponding to a command code. * * @param command the command code * * @return The POP3 protocol string command corresponding to a command code. */ public static String getCommand(final int command) { return commands[command]; } // Cannot be instantiated. private POP3Command() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/POP3MessageInfo.java000066400000000000000000000060221434047722200322770ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; /** * POP3MessageInfo is used to return information about messages stored on a POP3 server. Its fields are used to mean slightly different things depending on the * information being returned. *

* In response to a status command, number contains the number of messages in the mailbox, size contains the size of the mailbox * in bytes, and identifier is null. *

* In response to a message listings, number contains the message number, size contains the size of the message in bytes, and * identifier is null. *

* In response to unique identifier listings, number contains the message number, size is undefined, and identifier * contains the message's unique identifier. * * */ public final class POP3MessageInfo { public int number; public int size; public String identifier; /** * Creates a POP3MessageInfo instance with number and size set to 0, and identifier set to null. */ public POP3MessageInfo() { this(0, null, 0); } /** * Creates a POP3MessageInfo instance with number set to num , size set to octets , and * identifier set to null. * * @param num the number * @param octets the size */ public POP3MessageInfo(final int num, final int octets) { this(num, null, octets); } /** * Creates a POP3MessageInfo instance with number set to num , size undefined, and identifier set to * uid. * * @param num the number * @param uid the UID */ public POP3MessageInfo(final int num, final String uid) { this(num, uid, -1); } private POP3MessageInfo(final int num, final String uid, final int size) { this.number = num; this.size = size; this.identifier = uid; } /** * @since 3.6 */ @Override public String toString() { return "Number: " + number + ". Size: " + size + ". Id: " + identifier; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/POP3Reply.java000066400000000000000000000025021434047722200311710ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; /** * POP3Reply stores POP3 reply code constants. */ public final class POP3Reply { /** The reply code indicating success of an operation. */ public static final int OK = 0; /** The reply code indicating failure of an operation. */ public static final int ERROR = 1; /** * The reply code indicating intermediate response to a command. * * @since 3.0 */ public static final int OK_INT = 2; // Cannot be instantiated. private POP3Reply() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/POP3SClient.java000066400000000000000000000314671434047722200314530ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.util.SSLContextUtils; import org.apache.commons.net.util.SSLSocketUtils; /** * POP3 over SSL processing. Copied from FTPSClient.java and modified to suit POP3. If implicit mode is selected (NOT the default), SSL/TLS negotiation starts * right after the connection has been established. In explicit mode (the default), SSL/TLS negotiation starts when the user calls execTLS() and the server * accepts the command. Implicit usage: POP3SClient c = new POP3SClient(true); c.connect("127.0.0.1", 995); Explicit usage: POP3SClient c = new POP3SClient(); * c.connect("127.0.0.1", 110); if (c.execTLS()) { /rest of the commands here/ } * * Warning: the hostname is not verified against the certificate by default, use {@link #setHostnameVerifier(HostnameVerifier)} or * {@link #setEndpointCheckingEnabled(boolean)} (on Java 1.7+) to enable verification. * * @since 3.0 */ public class POP3SClient extends POP3Client { // from http://www.iana.org/assignments/port-numbers // pop3s 995/tcp pop3 protocol over TLS/SSL (was spop3) // pop3s 995/udp pop3 protocol over TLS/SSL (was spop3) private static final int DEFAULT_POP3S_PORT = 995; /** Default secure socket protocol name, like TLS */ private static final String DEFAULT_PROTOCOL = "TLS"; /** The security mode. True - Implicit Mode / False - Explicit Mode. */ private final boolean isImplicit; /** The secure socket protocol to be used, like SSL/TLS. */ private final String protocol; /** The context object. */ private SSLContext context; /** * The cipher suites. SSLSockets have a default set of these anyway, so no initialization required. */ private String[] suites; /** The protocol versions. */ private String[] protocols // null; ;// {"SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "SSLv2Hello"}; /** The FTPS {@link TrustManager} implementation, default null. */ private TrustManager trustManager; /** The {@link KeyManager}, default null. */ private KeyManager keyManager; /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ private HostnameVerifier hostnameVerifier; /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */ private boolean tlsEndpointChecking; /** * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS Sets security mode to explicit. */ public POP3SClient() { this(DEFAULT_PROTOCOL, false); } /** * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS * * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit */ public POP3SClient(final boolean implicit) { this(DEFAULT_PROTOCOL, implicit); } /** * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS * * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit * @param ctx A pre-configured SSL Context. */ public POP3SClient(final boolean implicit, final SSLContext ctx) { this(DEFAULT_PROTOCOL, implicit, ctx); } /** * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} - TLS - and isImplicit = false * * @param context A pre-configured SSL Context. * @see #POP3SClient(boolean, SSLContext) */ public POP3SClient(final SSLContext context) { this(false, context); } /** * Constructor for POP3SClient. Sets security mode to explicit. * * @param proto the protocol. */ public POP3SClient(final String proto) { this(proto, false); } /** * Constructor for POP3SClient. * * @param proto the protocol. * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit */ public POP3SClient(final String proto, final boolean implicit) { this(proto, implicit, null); } /** * Constructor for POP3SClient. Sets the default port to {@link #DEFAULT_POP3S_PORT} - 995 - if using implicit mode * * @param proto the protocol. * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit * @param ctx the context to be used */ public POP3SClient(final String proto, final boolean implicit, final SSLContext ctx) { protocol = proto; isImplicit = implicit; context = ctx; if (isImplicit) { setDefaultPort(DEFAULT_POP3S_PORT); } } /** * Because there are so many connect() methods, the _connectAction_() method is provided as a means of performing some action immediately after establishing * a connection, rather than reimplementing all of the connect() methods. * * @throws IOException If it is thrown by _connectAction_(). * @see org.apache.commons.net.SocketClient#_connectAction_() */ @Override protected void _connectAction_() throws IOException { // Implicit mode. if (isImplicit) { applySocketAttributes(); performSSLNegotiation(); } super._connectAction_(); // Explicit mode - don't do anything. The user calls execTLS() } /** * The TLS command execution. * * @throws SSLException If the server reply code is not positive. * @throws IOException If an I/O error occurs while sending the command or performing the negotiation. * @return TRUE if the command and negotiation succeeded. */ public boolean execTLS() throws SSLException, IOException { if (sendCommand("STLS") != POP3Reply.OK) { return false; // throw new SSLException(getReplyString()); } performSSLNegotiation(); return true; } /** * Returns the names of the cipher suites which could be enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is not an * {@link SSLSocket} instance, returns null. * * @return An array of cipher suite names, or null. */ public String[] getEnabledCipherSuites() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledCipherSuites(); } return null; } /** * Returns the names of the protocol versions which are currently enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is * not an {@link SSLSocket} instance, returns null. * * @return An array of protocols, or null. */ public String[] getEnabledProtocols() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledProtocols(); } return null; } /** * Get the currently configured {@link HostnameVerifier}. * * @return A HostnameVerifier instance. * @since 3.4 */ public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } /** * Get the {@link KeyManager} instance. * * @return The current {@link KeyManager} instance. */ private KeyManager getKeyManager() { return keyManager; } /** * Get the currently configured {@link TrustManager}. * * @return A TrustManager instance. */ public TrustManager getTrustManager() { return trustManager; } /** * Performs a lazy init of the SSL context. * * @throws IOException When could not initialize the SSL context. */ private void initSSLContext() throws IOException { if (context == null) { context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); } } /** * Return whether or not endpoint identification using the HTTPS algorithm on Java 1.7+ is enabled. The default behavior is for this to be disabled. * * @return True if enabled, false if not. * @since 3.4 */ public boolean isEndpointCheckingEnabled() { return tlsEndpointChecking; } /** * SSL/TLS negotiation. Acquires an SSL socket of a connection and carries out handshake processing. * * @throws IOException If server negotiation fails. */ private void performSSLNegotiation() throws IOException { initSSLContext(); final SSLSocketFactory ssf = context.getSocketFactory(); final String host = _hostname_ != null ? _hostname_ : getRemoteAddress().getHostAddress(); final int port = getRemotePort(); final SSLSocket socket = (SSLSocket) ssf.createSocket(_socket_, host, port, true); socket.setEnableSessionCreation(true); socket.setUseClientMode(true); if (tlsEndpointChecking) { SSLSocketUtils.enableEndpointNameVerification(socket); } if (protocols != null) { socket.setEnabledProtocols(protocols); } if (suites != null) { socket.setEnabledCipherSuites(suites); } socket.startHandshake(); // TODO the following setup appears to duplicate that in the super class methods _socket_ = socket; _input_ = socket.getInputStream(); _output_ = socket.getOutputStream(); reader = new CRLFLineReader(new InputStreamReader(_input_, DEFAULT_ENCODING)); writer = new BufferedWriter(new OutputStreamWriter(_output_, DEFAULT_ENCODING)); if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { throw new SSLHandshakeException("Hostname doesn't match certificate"); } } /** * Controls which particular cipher suites are enabled for use on this connection. Called before server negotiation. * * @param cipherSuites The cipher suites. */ public void setEnabledCipherSuites(final String[] cipherSuites) { suites = cipherSuites.clone(); } /** * Controls which particular protocol versions are enabled for use on this connection. I perform setting before a server negotiation. * * @param protocolVersions The protocol versions. */ public void setEnabledProtocols(final String[] protocolVersions) { protocols = protocolVersions.clone(); } /** * Automatic endpoint identification checking using the HTTPS algorithm is supported on Java 1.7+. The default behavior is for this to be disabled. * * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+. * @since 3.4 */ public void setEndpointCheckingEnabled(final boolean enable) { tlsEndpointChecking = enable; } /** * Override the default {@link HostnameVerifier} to use. * * @param newHostnameVerifier The HostnameVerifier implementation to set or null to disable. * @since 3.4 */ public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier) { hostnameVerifier = newHostnameVerifier; } /** * Set a {@link KeyManager} to use. * * @param newKeyManager The KeyManager implementation to set. * @see org.apache.commons.net.util.KeyManagerUtils */ public void setKeyManager(final KeyManager newKeyManager) { keyManager = newKeyManager; } /** * Override the default {@link TrustManager} to use. * * @param newTrustManager The TrustManager implementation to set. * @see org.apache.commons.net.util.TrustManagerUtils */ public void setTrustManager(final TrustManager newTrustManager) { trustManager = newTrustManager; } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/pop3/package-info.java000066400000000000000000000015471434047722200317700ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * POP3 and POP3S mail */ package org.apache.commons.net.pop3;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/000077500000000000000000000000001434047722200266745ustar00rootroot00000000000000AuthenticatingSMTPClient.java000066400000000000000000000321401434047722200342720ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import java.io.IOException; import java.net.InetAddress; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.SSLContext; import org.apache.commons.net.util.Base64; /** * An SMTP Client class with authentication support (RFC4954). * * @see SMTPClient * @since 3.0 */ public class AuthenticatingSMTPClient extends SMTPSClient { /** * The enumeration of currently-supported authentication methods. */ public enum AUTH_METHOD { /** The standarised (RFC4616) PLAIN method, which sends the password unencrypted (insecure). */ PLAIN, /** The standarised (RFC2195) CRAM-MD5 method, which doesn't send the password (secure). */ CRAM_MD5, /** The unstandarised Microsoft LOGIN method, which sends the password unencrypted (insecure). */ LOGIN, /** XOAuth method which accepts a signed and base64ed OAuth URL. */ XOAUTH, /** XOAuth 2 method which accepts a signed and base64ed OAuth JSON. */ XOAUTH2; /** * Gets the name of the given authentication method suitable for the server. * * @param method The authentication method to get the name for. * @return The name of the given authentication method suitable for the server. */ public static final String getAuthName(final AUTH_METHOD method) { if (method.equals(AUTH_METHOD.PLAIN)) { return "PLAIN"; } if (method.equals(AUTH_METHOD.CRAM_MD5)) { return "CRAM-MD5"; } if (method.equals(AUTH_METHOD.LOGIN)) { return "LOGIN"; } if (method.equals(AUTH_METHOD.XOAUTH)) { return "XOAUTH"; } if (method.equals(AUTH_METHOD.XOAUTH2)) { return "XOAUTH2"; } return null; } } /** * The default AuthenticatingSMTPClient constructor. Creates a new Authenticating SMTP Client. */ public AuthenticatingSMTPClient() { } /** * Overloaded constructor that takes the implicit argument, and using {@link #DEFAULT_PROTOCOL} i.e. TLS * * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit * @param ctx A pre-configured SSL Context. * @since 3.3 */ public AuthenticatingSMTPClient(final boolean implicit, final SSLContext ctx) { super(implicit, ctx); } /** * Overloaded constructor that takes a protocol specification * * @param protocol The protocol to use */ public AuthenticatingSMTPClient(final String protocol) { super(protocol); } /** * Overloaded constructor that takes a protocol specification and the implicit argument * * @param proto the protocol. * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit * @since 3.3 */ public AuthenticatingSMTPClient(final String proto, final boolean implicit) { super(proto, implicit); } /** * Overloaded constructor that takes the protocol specification, the implicit argument and encoding * * @param proto the protocol. * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit * @param encoding the encoding * @since 3.3 */ public AuthenticatingSMTPClient(final String proto, final boolean implicit, final String encoding) { super(proto, implicit, encoding); } /** * Overloaded constructor that takes a protocol specification and encoding * * @param protocol The protocol to use * @param encoding The encoding to use * @since 3.3 */ public AuthenticatingSMTPClient(final String protocol, final String encoding) { super(protocol, false, encoding); } /** * Authenticate to the SMTP server by sending the AUTH command with the selected mechanism, using the given username and the given password. * * @param method the method to use, one of the {@link AuthenticatingSMTPClient.AUTH_METHOD} enum values * @param username the user name. If the method is XOAUTH/XOAUTH2, then this is used as the plain text oauth protocol parameter string which is * Base64-encoded for transmission. * @param password the password for the username. Ignored for XOAUTH/XOAUTH2. * * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @throws NoSuchAlgorithmException If the CRAM hash algorithm cannot be instantiated by the Java runtime system. * @throws InvalidKeyException If the CRAM hash algorithm failed to use the given password. * @throws InvalidKeySpecException If the CRAM hash algorithm failed to use the given password. */ public boolean auth(final AuthenticatingSMTPClient.AUTH_METHOD method, final String username, final String password) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException { if (!SMTPReply.isPositiveIntermediate(sendCommand(SMTPCommand.AUTH, AUTH_METHOD.getAuthName(method)))) { return false; } if (method.equals(AUTH_METHOD.PLAIN)) { // the server sends an empty response ("334 "), so we don't have to read it. return SMTPReply .isPositiveCompletion(sendCommand(Base64.encodeBase64StringUnChunked(("\000" + username + "\000" + password).getBytes(getCharset())))); } if (method.equals(AUTH_METHOD.CRAM_MD5)) { // get the CRAM challenge final byte[] serverChallenge = Base64.decodeBase64(getReplyString().substring(4).trim()); // get the Mac instance final Mac hmac_md5 = Mac.getInstance("HmacMD5"); hmac_md5.init(new SecretKeySpec(password.getBytes(getCharset()), "HmacMD5")); // compute the result: final byte[] hmacResult = convertToHexString(hmac_md5.doFinal(serverChallenge)).getBytes(getCharset()); // join the byte arrays to form the reply final byte[] usernameBytes = username.getBytes(getCharset()); final byte[] toEncode = new byte[usernameBytes.length + 1 /* the space */ + hmacResult.length]; System.arraycopy(usernameBytes, 0, toEncode, 0, usernameBytes.length); toEncode[usernameBytes.length] = ' '; System.arraycopy(hmacResult, 0, toEncode, usernameBytes.length + 1, hmacResult.length); // send the reply and read the server code: return SMTPReply.isPositiveCompletion(sendCommand(Base64.encodeBase64StringUnChunked(toEncode))); } if (method.equals(AUTH_METHOD.LOGIN)) { // the server sends fixed responses (base64("Username") and // base64("Password")), so we don't have to read them. if (!SMTPReply.isPositiveIntermediate(sendCommand(Base64.encodeBase64StringUnChunked(username.getBytes(getCharset()))))) { return false; } return SMTPReply.isPositiveCompletion(sendCommand(Base64.encodeBase64StringUnChunked(password.getBytes(getCharset())))); } if (method.equals(AUTH_METHOD.XOAUTH) || method.equals(AUTH_METHOD.XOAUTH2)) { return SMTPReply.isPositiveIntermediate(sendCommand(Base64.encodeBase64StringUnChunked(username.getBytes(getCharset())))); } return false; // safety check } /** * Converts the given byte array to a String containing the hex values of the bytes. For example, the byte 'A' will be converted to '41', because this is * the ASCII code (and the byte value) of the capital letter 'A'. * * @param a The byte array to convert. * @return The resulting String of hex codes. */ private String convertToHexString(final byte[] a) { final StringBuilder result = new StringBuilder(a.length * 2); for (final byte element : a) { if ((element & 0x0FF) <= 15) { result.append("0"); } result.append(Integer.toHexString(element & 0x0FF)); } return result.toString(); } /** * A convenience method to send the ESMTP EHLO command to the server, receive the reply, and return the reply code. *

* * @param hostname The hostname of the sender. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int ehlo(final String hostname) throws IOException { return sendCommand(SMTPCommand.EHLO, hostname); } /** * Login to the ESMTP server by sending the EHLO command with the client hostname as an argument. Before performing any mail commands, you must first login. *

* * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean elogin() throws IOException { final String name; final InetAddress host; host = getLocalAddress(); name = host.getHostName(); if (name == null) { return false; } return SMTPReply.isPositiveCompletion(ehlo(name)); } /** * Login to the ESMTP server by sending the EHLO command with the given hostname as an argument. Before performing any mail commands, you must first login. *

* * @param hostname The hostname with which to greet the SMTP server. * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean elogin(final String hostname) throws IOException { return SMTPReply.isPositiveCompletion(ehlo(hostname)); } /** * Returns the integer values of the enhanced reply code of the last SMTP reply. * * @return The integer values of the enhanced reply code of the last SMTP reply. First digit is in the first array element. */ public int[] getEnhancedReplyCode() { final String reply = getReplyString().substring(4); final String[] parts = reply.substring(0, reply.indexOf(' ')).split("\\."); final int[] res = new int[parts.length]; Arrays.setAll(res, i -> Integer.parseInt(parts[i])); return res; } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/RelayPath.java000066400000000000000000000056271434047722200314420ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import java.util.Enumeration; import java.util.Vector; /** * A class used to represent forward and reverse relay paths. The SMTP MAIL command requires a reverse relay path while the SMTP RCPT command requires a forward * relay path. See RFC 821 for more details. In general, you will not have to deal with relay paths. * * @see SMTPClient */ public final class RelayPath { private final Vector path; private final String emailAddress; /** * Create a relay path with the specified email address as the ultimate destination. *

* * @param emailAddress The destination email address. */ public RelayPath(final String emailAddress) { this.path = new Vector<>(); this.emailAddress = emailAddress; } /** * Add a mail relay host to the relay path. Hosts are added left to right. For example, the following will create the path * < @bar.com,@foo.com:foobar@foo.com > * *

     * path = new RelayPath("foobar@foo.com");
     * path.addRelay("bar.com");
     * path.addRelay("foo.com");
     * 
*

* * @param hostname The host to add to the relay path. */ public void addRelay(final String hostname) { path.addElement(hostname); } /** * Return the properly formatted string representation of the relay path. *

* * @return The properly formatted string representation of the relay path. */ @Override public String toString() { final StringBuilder buffer = new StringBuilder(); final Enumeration hosts; buffer.append('<'); hosts = path.elements(); if (hosts.hasMoreElements()) { buffer.append('@'); buffer.append(hosts.nextElement()); while (hosts.hasMoreElements()) { buffer.append(",@"); buffer.append(hosts.nextElement()); } buffer.append(':'); } buffer.append(emailAddress); buffer.append('>'); return buffer.toString(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/SMTP.java000066400000000000000000000745541434047722200303410ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; import org.apache.commons.net.MalformedServerReplyException; import org.apache.commons.net.ProtocolCommandSupport; import org.apache.commons.net.SocketClient; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.util.NetConstants; /** * SMTP provides the basic the functionality necessary to implement your own SMTP client. To derive the full benefits of the SMTP class requires some knowledge * of the FTP protocol defined in RFC 821. However, there is no reason why you should have to use the SMTP class. The * {@link org.apache.commons.net.smtp.SMTPClient} class, derived from SMTP, implements all the functionality required of an SMTP client. The SMTP class is made * public to provide access to various SMTP constants and to make it easier for adventurous programmers (or those with special needs) to interact with the SMTP * protocol and implement their own clients. A set of methods with names corresponding to the SMTP command names are provided to facilitate this interaction. *

* You should keep in mind that the SMTP server may choose to prematurely close a connection for various reasons. The SMTP class will detect a premature SMTP * server connection closing when it receives a {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE } response to * a command. When that occurs, the SMTP class method encountering that reply will throw an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} . * SMTPConectionClosedException is a subclass of IOException and therefore need not be caught separately, but if you are going to * catch it separately, its catch block must appear before the more general IOException catch block. When you encounter an * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} , you must disconnect the connection with * {@link org.apache.commons.net.SocketClient#disconnect disconnect() } to properly clean up the system resources used by SMTP. Before disconnecting, you may * check the last reply code and text with {@link #getReplyCode getReplyCode }, {@link #getReplyString getReplyString }, and {@link #getReplyStrings * getReplyStrings}. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. * * @see SMTPClient * @see SMTPConnectionClosedException * @see org.apache.commons.net.MalformedServerReplyException */ public class SMTP extends SocketClient { /** The default SMTP port (25). */ public static final int DEFAULT_PORT = 25; // We have to ensure that the protocol communication is in ASCII // but we use ISO-8859-1 just in case 8-bit characters cross // the wire. private static final String DEFAULT_ENCODING = "ISO-8859-1"; /** * The encoding to use (user-settable). * * @since 3.1 (changed from private to protected) */ protected final String encoding; /** * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and te firing of ProtocolCommandEvents. */ protected ProtocolCommandSupport _commandSupport_; BufferedReader reader; BufferedWriter writer; private int replyCode; private final ArrayList replyLines; private boolean newReplyString; private String replyString; /** * The default SMTP constructor. Sets the default port to DEFAULT_PORT and initializes internal data structures for saving SMTP reply * information. */ public SMTP() { this(DEFAULT_ENCODING); } /** * Overloaded constructor where the user may specify a default encoding. * * @param encoding the encoing to use * @since 2.0 */ public SMTP(final String encoding) { setDefaultPort(DEFAULT_PORT); replyLines = new ArrayList<>(); newReplyString = false; replyString = null; _commandSupport_ = new ProtocolCommandSupport(this); this.encoding = encoding; } /** Initiates control connections and gets initial reply. */ @Override protected void _connectAction_() throws IOException { super._connectAction_(); reader = new CRLFLineReader(new InputStreamReader(_input_, encoding)); writer = new BufferedWriter(new OutputStreamWriter(_output_, encoding)); getReply(); } /** * A convenience method to send the SMTP DATA command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int data() throws IOException { return sendCommand(SMTPCommand.DATA); } /** * Closes the connection to the SMTP server and sets to null some internal data so that the memory may be reclaimed by the garbage collector. The reply text * and code information from the last command is voided so that the memory it used may be reclaimed. *

* * @throws IOException If an error occurs while disconnecting. */ @Override public void disconnect() throws IOException { super.disconnect(); reader = null; writer = null; replyString = null; replyLines.clear(); newReplyString = false; } /** * A convenience method to send the SMTP VRFY command to the server, receive the reply, and return the reply code. *

* * @param name The name to expand. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int expn(final String name) throws IOException { return sendCommand(SMTPCommand.EXPN, name); } /** * Provide command support to super-class */ @Override protected ProtocolCommandSupport getCommandSupport() { return _commandSupport_; } /** * Fetches a reply from the SMTP server and returns the integer reply code. After calling this method, the actual reply text can be accessed from either * calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. Only use this method if you are implementing your own SMTP * client or if you need to fetch a secondary response from the SMTP server. *

* * @return The integer value of the reply code of the fetched SMTP reply. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while receiving the server reply. */ public int getReply() throws IOException { final int length; newReplyString = true; replyLines.clear(); String line = reader.readLine(); if (line == null) { throw new SMTPConnectionClosedException("Connection closed without indication."); } // In case we run into an anomaly we don't want fatal index exceptions // to be thrown. length = line.length(); if (length < 3) { throw new MalformedServerReplyException("Truncated server reply: " + line); } try { final String code = line.substring(0, 3); replyCode = Integer.parseInt(code); } catch (final NumberFormatException e) { throw new MalformedServerReplyException("Could not parse response code.\nServer Reply: " + line); } replyLines.add(line); // Get extra lines if message continues. if (length > 3 && line.charAt(3) == '-') { do { line = reader.readLine(); if (line == null) { throw new SMTPConnectionClosedException("Connection closed without indication."); } replyLines.add(line); // The length() check handles problems that could arise from readLine() // returning too soon after encountering a naked CR or some other // anomaly. } while (!(line.length() >= 4 && line.charAt(3) != '-' && Character.isDigit(line.charAt(0)))); // This is too strong a condition because a non-conforming server // could screw things up like ftp.funet.fi does for FTP // line.startsWith(code))); } fireReplyReceived(replyCode, getReplyString()); if (replyCode == SMTPReply.SERVICE_NOT_AVAILABLE) { throw new SMTPConnectionClosedException("SMTP response 421 received. Server closed connection."); } return replyCode; } /** * Returns the integer value of the reply code of the last SMTP reply. You will usually only use this method after you connect to the SMTP server to check * that the connection was successful since connect is of type void. *

* * @return The integer value of the reply code of the last SMTP reply. */ public int getReplyCode() { return replyCode; } /** * Returns the entire text of the last SMTP server response exactly as it was received, including all end of line markers in NETASCII format. *

* * @return The entire text from the last SMTP response as a String. */ public String getReplyString() { final StringBuilder buffer; if (!newReplyString) { return replyString; } buffer = new StringBuilder(); for (final String line : replyLines) { buffer.append(line); buffer.append(SocketClient.NETASCII_EOL); } newReplyString = false; replyString = buffer.toString(); return replyString; } /** * Returns the lines of text from the last SMTP server response as an array of strings, one entry per line. The end of line markers of each are stripped * from each line. *

* * @return The lines of text from the last SMTP response as an array. */ public String[] getReplyStrings() { return replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY); } /** * A convenience method to send the SMTP HELO command to the server, receive the reply, and return the reply code. *

* * @param hostname The hostname of the sender. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int helo(final String hostname) throws IOException { return sendCommand(SMTPCommand.HELO, hostname); } /** * A convenience method to send the SMTP HELP command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int help() throws IOException { return sendCommand(SMTPCommand.HELP); } /** * A convenience method to send the SMTP HELP command to the server, receive the reply, and return the reply code. *

* * @param command The command name on which to request help. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int help(final String command) throws IOException { return sendCommand(SMTPCommand.HELP, command); } /** * A convenience method to send the SMTP MAIL command to the server, receive the reply, and return the reply code. *

* * @param reversePath The reverese path. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int mail(final String reversePath) throws IOException { return sendCommand(SMTPCommand.MAIL, reversePath, false); } /** * A convenience method to send the SMTP NOOP command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int noop() throws IOException { return sendCommand(SMTPCommand.NOOP); } /** * A convenience method to send the SMTP QUIT command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int quit() throws IOException { return sendCommand(SMTPCommand.QUIT); } /** * A convenience method to send the SMTP RCPT command to the server, receive the reply, and return the reply code. *

* * @param forwardPath The forward path. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int rcpt(final String forwardPath) throws IOException { return sendCommand(SMTPCommand.RCPT, forwardPath, false); } /** * Removes a ProtocolCommandListener. * * Delegates this incorrectly named method - removeProtocolCommandistener (note the missing "L")- to the correct method * {@link SocketClient#removeProtocolCommandListener} * * @param listener The ProtocolCommandListener to remove */ public void removeProtocolCommandistener(final org.apache.commons.net.ProtocolCommandListener listener) { removeProtocolCommandListener(listener); } /** * A convenience method to send the SMTP RSET command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int rset() throws IOException { return sendCommand(SMTPCommand.RSET); } /** * A convenience method to send the SMTP SAML command to the server, receive the reply, and return the reply code. *

* * @param reversePath The reverese path. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int saml(final String reversePath) throws IOException { return sendCommand(SMTPCommand.SAML, reversePath); } /** * A convenience method to send the SMTP SEND command to the server, receive the reply, and return the reply code. *

* * @param reversePath The reverese path. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int send(final String reversePath) throws IOException { return sendCommand(SMTPCommand.SEND, reversePath); } /** * Sends an SMTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. *

* * @param command The SMTPCommand constant corresponding to the SMTP command to send. * @return The integer value of the SMTP reply code returned by the server in response to the command. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final int command) throws IOException { return sendCommand(command, null); } /** * Sends an SMTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. *

* * @param command The SMTPCommand constant corresponding to the SMTP command to send. * @param args The arguments to the SMTP command. If this parameter is set to null, then the command is sent with no argument. * @return The integer value of the SMTP reply code returned by the server in response to the command. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final int command, final String args) throws IOException { return sendCommand(SMTPCommand.getCommand(command), args); } /** * * @param command the command to send (as an int defined in {@link SMPTCommand}) * @param args the command arguments, may be {@code null} * @param includeSpace if {@code true}, add a space between the command and its arguments * @return the reply code * @throws IOException */ private int sendCommand(final int command, final String args, final boolean includeSpace) throws IOException { return sendCommand(SMTPCommand.getCommand(command), args, includeSpace); } /** * Sends an SMTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. *

* * @param command The text representation of the SMTP command to send. * @return The integer value of the SMTP reply code returned by the server in response to the command. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final String command) throws IOException { return sendCommand(command, null); } /** * Sends an SMTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. *

* * @param command The text representation of the SMTP command to send. * @param args The arguments to the SMTP command. If this parameter is set to null, then the command is sent with no argument. * @return The integer value of the SMTP reply code returned by the server in response to the command. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int sendCommand(final String command, final String args) throws IOException { return sendCommand(command, args, true); } /** * Send a command to the server. May also be used to send text data. * * @param command the command to send (as a plain String) * @param args the command arguments, may be {@code null} * @param includeSpace if {@code true}, add a space between the command and its arguments * @return the reply code * @throws IOException */ private int sendCommand(final String command, final String args, final boolean includeSpace) throws IOException { final StringBuilder __commandBuffer = new StringBuilder(); __commandBuffer.append(command); if (args != null) { if (includeSpace) { __commandBuffer.append(' '); } __commandBuffer.append(args); } __commandBuffer.append(SocketClient.NETASCII_EOL); final String message = __commandBuffer.toString(); writer.write(message); writer.flush(); fireCommandSent(command, message); return getReply(); } /** * A convenience method to send the SMTP SOML command to the server, receive the reply, and return the reply code. *

* * @param reversePath The reverese path. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int soml(final String reversePath) throws IOException { return sendCommand(SMTPCommand.SOML, reversePath); } /** * A convenience method to send the SMTP TURN command to the server, receive the reply, and return the reply code. *

* * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int turn() throws IOException { return sendCommand(SMTPCommand.TURN); } /** * A convenience method to send the SMTP VRFY command to the server, receive the reply, and return the reply code. *

* * @param user The user address to verify. * @return The reply code received from the server. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply. */ public int vrfy(final String user) throws IOException { return sendCommand(SMTPCommand.VRFY, user); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/SMTPClient.java000066400000000000000000000637531434047722200314770ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import java.io.IOException; import java.io.Writer; import java.net.InetAddress; import org.apache.commons.net.io.DotTerminatedMessageWriter; /** * SMTPClient encapsulates all the functionality necessary to send files through an SMTP server. This class takes care of all low level details of interacting * with an SMTP server and provides a convenient higher level interface. As with all classes derived from {@link org.apache.commons.net.SocketClient}, you must * first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before doing anything, and finally * {@link org.apache.commons.net.SocketClient#disconnect disconnect } after you're completely finished interacting with the server. Then you need to check the * SMTP reply code to see if the connection was successful. For example: * *

 *    try {
 *      int reply;
 *      client.connect("mail.foobar.com");
 *      System.out.print(client.getReplyString());
 *
 *      // After connection attempt, you should check the reply code to verify
 *      // success.
 *      reply = client.getReplyCode();
 *
 *      if(!SMTPReply.isPositiveCompletion(reply)) {
 *        client.disconnect();
 *        System.err.println("SMTP server refused connection.");
 *        System.exit(1);
 *      }
 *
 *      // Do useful stuff here.
 *      ...
 *    } catch(IOException e) {
 *      if(client.isConnected()) {
 *        try {
 *          client.disconnect();
 *        } catch(IOException f) {
 *          // do nothing
 *        }
 *      }
 *      System.err.println("Could not connect to server.");
 *      e.printStackTrace();
 *      System.exit(1);
 *    }
 * 
*

* Immediately after connecting is the only real time you need to check the reply code (because connect is of type void). The convention for all the SMTP * command methods in SMTPClient is such that they either return a boolean value or some other value. The boolean methods return true on a successful completion * reply from the SMTP server and false on a reply resulting in an error condition or failure. The methods returning a value other than boolean return a value * containing the higher level data produced by the SMTP command, or null if a reply resulted in an error condition or failure. If you want to access the exact * SMTP reply code causing a success or failure, you must call {@link org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode } after a success or failure. *

* You should keep in mind that the SMTP server may choose to prematurely close a connection for various reasons. The SMTPClient class will detect a premature * SMTP server connection closing when it receives a {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE } * response to a command. When that occurs, the method encountering that reply will throw an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} . * SMTPConectionClosedException is a subclass of IOException and therefore need not be caught separately, but if you are going to * catch it separately, its catch block must appear before the more general IOException catch block. When you encounter an * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} , you must disconnect the connection with {@link #disconnect disconnect() } to properly * clean up the system resources used by SMTPClient. Before disconnecting, you may check the last reply code and text with * {@link org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode }, {@link org.apache.commons.net.smtp.SMTP#getReplyString getReplyString }, and * {@link org.apache.commons.net.smtp.SMTP#getReplyStrings getReplyStrings}. *

* Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as * lenient as possible. * * @see SMTP * @see SimpleSMTPHeader * @see RelayPath * @see SMTPConnectionClosedException * @see org.apache.commons.net.MalformedServerReplyException */ public class SMTPClient extends SMTP { /** * Default SMTPClient constructor. Creates a new SMTPClient instance. */ public SMTPClient() { } /** * Overloaded constructor that takes an encoding specification * * @param encoding The encoding to use * @since 2.0 */ public SMTPClient(final String encoding) { super(encoding); } /** * Add a recipient for a message using the SMTP RCPT command, specifying a forward relay path. The sender must be set first before any recipients may be * specified, otherwise the mail server will reject your commands. *

* * @param path The forward relay path pointing to the recipient. * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean addRecipient(final RelayPath path) throws IOException { return SMTPReply.isPositiveCompletion(rcpt(path.toString())); } /** * Add a recipient for a message using the SMTP RCPT command, the recipient's email address. The sender must be set first before any recipients may be * specified, otherwise the mail server will reject your commands. *

* * @param address The recipient's email address. * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean addRecipient(final String address) throws IOException { return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">")); } /** * At least one SMTPClient method ({@link #sendMessageData sendMessageData }) does not complete the entire sequence of SMTP commands to complete a * transaction. These types of commands require some action by the programmer after the reception of a positive intermediate command. After the programmer's * code completes its actions, it must call this method to receive the completion reply from the server and verify the success of the entire transaction. *

* For example, * *

     * writer = client.sendMessageData();
     * if (writer == null) // failure
     *     return false;
     * header = new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
     * writer.write(header.toString());
     * writer.write("This is just a test");
     * writer.close();
     * if (!client.completePendingCommand()) // failure
     *     return false;
     * 
*

* * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean completePendingCommand() throws IOException { return SMTPReply.isPositiveCompletion(getReply()); } /** * Fetches the system help information from the server and returns the full string. *

* * @return The system help string obtained from the server. null if the information could not be obtained. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String listHelp() throws IOException { if (SMTPReply.isPositiveCompletion(help())) { return getReplyString(); } return null; } /** * Fetches the help information for a given command from the server and returns the full string. *

* * @param command The command on which to ask for help. * @return The command help string obtained from the server. null if the information could not be obtained. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public String listHelp(final String command) throws IOException { if (SMTPReply.isPositiveCompletion(help(command))) { return getReplyString(); } return null; } /** * Login to the SMTP server by sending the HELO command with the client hostname as an argument. Before performing any mail commands, you must first login. *

* * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean login() throws IOException { final String name; final InetAddress host; host = getLocalAddress(); name = host.getHostName(); if (name == null) { return false; } return SMTPReply.isPositiveCompletion(helo(name)); } /** * Login to the SMTP server by sending the HELO command with the given hostname as an argument. Before performing any mail commands, you must first login. *

* * @param hostname The hostname with which to greet the SMTP server. * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean login(final String hostname) throws IOException { return SMTPReply.isPositiveCompletion(helo(hostname)); } /** * Logout of the SMTP server by sending the QUIT command. *

* * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean logout() throws IOException { return SMTPReply.isPositiveCompletion(quit()); } /** * Aborts the current mail transaction, resetting all server stored sender, recipient, and mail data, cleaing all buffers and tables. *

* * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean reset() throws IOException { return SMTPReply.isPositiveCompletion(rset()); } /** * Send the SMTP DATA command in preparation to send an email message. This method returns a DotTerminatedMessageWriter instance to which the message can be * written. Null is returned if the DATA command fails. *

* You must not issue any commands to the SMTP server (i.e., call any (other methods) until you finish writing to the returned Writer instance and close it. * The SMTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned Writer actually writes directly to * the SMTP connection. After you close the writer, you can execute new commands. If you do not follow these requirements your program will not work * properly. *

* You can use the provided {@link org.apache.commons.net.smtp.SimpleSMTPHeader} class to construct a bare minimum header. To construct more complicated * headers you should refer to RFC 5322. When the Java Mail API is finalized, you will be able to use it to compose fully compliant Internet text messages. * The DotTerminatedMessageWriter takes care of doubling line-leading dots and ending the message with a single dot upon closing, so all you have to worry * about is writing the header and the message. *

* Upon closing the returned Writer, you need to call {@link #completePendingCommand completePendingCommand() } to finalize the transaction and verify its * success or failure from the server reply. *

* * @return A DotTerminatedMessageWriter to which the message (including header) can be written. Returns null if the command fails. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. * @see #sendShortMessageData(String) */ public Writer sendMessageData() throws IOException { if (!SMTPReply.isPositiveIntermediate(data())) { return null; } return new DotTerminatedMessageWriter(writer); } /** * Sends a NOOP command to the SMTP server. This is useful for preventing server timeouts. *

* * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean sendNoOp() throws IOException { return SMTPReply.isPositiveCompletion(noop()); } /** * A convenience method for sending short messages. This method fetches the Writer returned by {@link #sendMessageData sendMessageData() } and writes the * specified String to it. After writing the message, this method calls {@link #completePendingCommand completePendingCommand() } to finalize the * transaction and returns its success or failure. *

* * @param message The short email message to send. This must include the headers and the body, but not the trailing "." * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean sendShortMessageData(final String message) throws IOException { try (final Writer writer = sendMessageData()) { if (writer == null) { return false; } writer.write(message); } return completePendingCommand(); } /** * A convenience method for a sending short email without having to explicitly set the sender and recipient(s). This method sets the sender and recipient * using {@link #setSender setSender } and {@link #addRecipient addRecipient }, and then sends the message using {@link #sendShortMessageData * sendShortMessageData }. *

* * @param sender The email address of the sender. * @param recipient The email address of the recipient. * @param message The short email message to send. This must include the headers and the body, but not the trailing "." * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean sendSimpleMessage(final String sender, final String recipient, final String message) throws IOException { if (!setSender(sender)) { return false; } if (!addRecipient(recipient)) { return false; } return sendShortMessageData(message); } /** * A convenience method for a sending short email without having to explicitly set the sender and recipient(s). This method sets the sender and recipients * using {@link #setSender(String) setSender} and {@link #addRecipient(String) addRecipient}, and then sends the message using * {@link #sendShortMessageData(String) sendShortMessageData}. *

* Note that the method ignores failures when calling {@link #addRecipient(String) addRecipient} so long as at least one call succeeds. If no recipients can * be successfully added then the method will fail (and does not attempt to send the message) *

* * @param sender The email address of the sender. * @param recipients An array of recipient email addresses. * @param message The short email message to send. This must include the headers and the body, but not the trailing "." * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean sendSimpleMessage(final String sender, final String[] recipients, final String message) throws IOException { boolean oneSuccess = false; int count; if (!setSender(sender)) { return false; } for (count = 0; count < recipients.length; count++) { if (addRecipient(recipients[count])) { oneSuccess = true; } } if (!oneSuccess) { return false; } return sendShortMessageData(message); } /** * Set the sender of a message using the SMTP MAIL command, specifying a reverse relay path. The sender must be set first before any recipients may be * specified, otherwise the mail server will reject your commands. *

* * @param path The reverse relay path pointing back to the sender. * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean setSender(final RelayPath path) throws IOException { return SMTPReply.isPositiveCompletion(mail(path.toString())); } /** * Set the sender of a message using the SMTP MAIL command, specifying the sender's email address. The sender must be set first before any recipients may be * specified, otherwise the mail server will reject your commands. *

* * @param address The sender's email address. * @return True if successfully completed, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean setSender(final String address) throws IOException { return SMTPReply.isPositiveCompletion(mail("<" + address + ">")); } /** * Verify that a username or email address is valid, i.e., that mail can be delivered to that mailbox on the server. *

* * @param username The username or email address to validate. * @return True if the username is valid, false if not. * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or * independently as itself. * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. */ public boolean verify(final String username) throws IOException { final int result; result = vrfy(username); return result == SMTPReply.ACTION_OK || result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/SMTPCommand.java000066400000000000000000000071171434047722200316270ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; /** * SMTPCommand stores a set of constants for SMTP command codes. To interpret the meaning of the codes, familiarity with RFC 821 is assumed. The mnemonic * constant names are transcriptions from the code descriptions of RFC 821. For those who think in terms of the actual SMTP commands, a set of constants such as * {@link #HELO HELO } are provided where the constant name is the same as the SMTP command. */ public final class SMTPCommand { public static final int HELO = 0; public static final int MAIL = 1; public static final int RCPT = 2; public static final int DATA = 3; public static final int SEND = 4; public static final int SOML = 5; public static final int SAML = 6; public static final int RSET = 7; public static final int VRFY = 8; public static final int EXPN = 9; public static final int HELP = 10; public static final int NOOP = 11; public static final int TURN = 12; public static final int QUIT = 13; /** * The authorization command * * @since 3.0 */ public static final int AUTH = 14; /** * The extended hello command * * @since 3.0 */ public static final int EHLO = 15; private static final int NEXT = EHLO + 1; // update as necessary when adding new entries public static final int HELLO = HELO; public static final int LOGIN = HELO; public static final int MAIL_FROM = MAIL; public static final int RECIPIENT = RCPT; public static final int SEND_MESSAGE_DATA = DATA; public static final int SEND_FROM = SEND; public static final int SEND_OR_MAIL_FROM = SOML; public static final int SEND_AND_MAIL_FROM = SAML; public static final int RESET = RSET; public static final int VERIFY = VRFY; public static final int EXPAND = EXPN; // public static final int HELP = HELP; // public static final int NOOP = NOOP; // public static final int TURN = TURN; // public static final int QUIT = QUIT; public static final int LOGOUT = QUIT; private static final String[] commands = { "HELO", "MAIL FROM:", "RCPT TO:", "DATA", "SEND FROM:", "SOML FROM:", "SAML FROM:", "RSET", "VRFY", "EXPN", "HELP", "NOOP", "TURN", "QUIT", "AUTH", "EHLO" }; static { if (commands.length != NEXT) { throw new RuntimeException("Error in array definition"); } } /** * Retrieve the SMTP protocol command string corresponding to a specified command code. *

* * @param command The command code. * @return The SMTP protcol command string corresponding to a specified command code. */ public static String getCommand(final int command) { return commands[command]; } // Cannot be instantiated private SMTPCommand() { } } SMTPConnectionClosedException.java000066400000000000000000000035501434047722200352770ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import java.io.IOException; /** * SMTPConnectionClosedException is used to indicate the premature or unexpected closing of an SMTP connection resulting from a * {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE } response (SMTP reply code 421) to a failed SMTP command. * This exception is derived from IOException and therefore may be caught either as an IOException or specifically as an SMTPConnectionClosedException. * * * @see SMTP * @see SMTPClient */ public final class SMTPConnectionClosedException extends IOException { private static final long serialVersionUID = 626520434326660627L; /** Constructs a SMTPConnectionClosedException with no message */ public SMTPConnectionClosedException() { } /** * Constructs a SMTPConnectionClosedException with a specified message. * * @param message The message explaining the reason for the exception. */ public SMTPConnectionClosedException(final String message) { super(message); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/SMTPReply.java000066400000000000000000000125041434047722200313400ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; /** * SMTPReply stores a set of constants for SMTP reply codes. To interpret the meaning of the codes, familiarity with RFC 821 is assumed. The mnemonic constant * names are transcriptions from the code descriptions of RFC 821. */ public final class SMTPReply { public static final int SYSTEM_STATUS = 211; public static final int HELP_MESSAGE = 214; public static final int SERVICE_READY = 220; public static final int SERVICE_CLOSING_TRANSMISSION_CHANNEL = 221; public static final int ACTION_OK = 250; public static final int USER_NOT_LOCAL_WILL_FORWARD = 251; public static final int START_MAIL_INPUT = 354; public static final int SERVICE_NOT_AVAILABLE = 421; public static final int ACTION_NOT_TAKEN = 450; public static final int ACTION_ABORTED = 451; public static final int INSUFFICIENT_STORAGE = 452; public static final int UNRECOGNIZED_COMMAND = 500; public static final int SYNTAX_ERROR_IN_ARGUMENTS = 501; public static final int COMMAND_NOT_IMPLEMENTED = 502; public static final int BAD_COMMAND_SEQUENCE = 503; public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504; public static final int MAILBOX_UNAVAILABLE = 550; public static final int USER_NOT_LOCAL = 551; public static final int STORAGE_ALLOCATION_EXCEEDED = 552; public static final int MAILBOX_NAME_NOT_ALLOWED = 553; public static final int TRANSACTION_FAILED = 554; /** * Determine if a reply code is a negative permanent response. All codes beginning with a 5 are negative permanent responses. The SMTP server will send a * negative permanent response on the failure of a command that cannot be reattempted with success. *

* * @param reply The reply code to test. * @return True if a reply code is a negative permanent response, false if not. */ public static boolean isNegativePermanent(final int reply) { return reply >= 500 && reply < 600; } /** * Determine if a reply code is a negative transient response. All codes beginning with a 4 are negative transient responses. The SMTP server will send a * negative transient response on the failure of a command that can be reattempted with success. *

* * @param reply The reply code to test. * @return True if a reply code is a negative transient response, false if not. */ public static boolean isNegativeTransient(final int reply) { return reply >= 400 && reply < 500; } /** * Determine if a reply code is a positive completion response. All codes beginning with a 2 are positive completion responses. The SMTP server will send a * positive completion response on the final successful completion of a command. *

* * @param reply The reply code to test. * @return True if a reply code is a positive completion response, false if not. */ public static boolean isPositiveCompletion(final int reply) { return reply >= 200 && reply < 300; } /** * Determine if a reply code is a positive intermediate response. All codes beginning with a 3 are positive intermediate responses. The SMTP server will * send a positive intermediate response on the successful completion of one part of a multi-part sequence of commands. For example, after a successful DATA * command, a positive intermediate response will be sent to indicate that the server is ready to receive the message data. *

* * @param reply The reply code to test. * @return True if a reply code is a positive intermediate response, false if not. */ public static boolean isPositiveIntermediate(final int reply) { return reply >= 300 && reply < 400; } /** * Determine if a reply code is a positive preliminary response. All codes beginning with a 1 are positive preliminary responses. Postitive preliminary * responses are used to indicate tentative success. No further commands can be issued to the SMTP server after a positive preliminary response until a * follow up response is received from the server. *

* Note: No SMTP commands defined in RFC 822 provide this type of reply. *

* * @param reply The reply code to test. * @return True if a reply code is a positive preliminary response, false if not. */ public static boolean isPositivePreliminary(final int reply) { return reply >= 100 && reply < 200; } // Cannot be instantiated private SMTPReply() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/SMTPSClient.java000066400000000000000000000306531434047722200316130ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.apache.commons.net.io.CRLFLineReader; import org.apache.commons.net.util.SSLContextUtils; import org.apache.commons.net.util.SSLSocketUtils; /** * SMTP over SSL processing. Copied from FTPSClient.java and modified to suit SMTP. If implicit mode is selected (NOT the default), SSL/TLS negotiation starts * right after the connection has been established. In explicit mode (the default), SSL/TLS negotiation starts when the user calls execTLS() and the server * accepts the command. Implicit usage: * *

 * SMTPSClient c = new SMTPSClient(true);
 * c.connect("127.0.0.1", 465);
 * 
* * Explicit usage: * *
 * SMTPSClient c = new SMTPSClient();
 * c.connect("127.0.0.1", 25);
 * if (c.execTLS()) {
 *     // Rest of the commands here
 * }
 * 
* * Warning: the hostname is not verified against the certificate by default, use {@link #setHostnameVerifier(HostnameVerifier)} or * {@link #setEndpointCheckingEnabled(boolean)} (on Java 1.7+) to enable verification. * * @since 3.0 */ public class SMTPSClient extends SMTPClient { /** Default secure socket protocol name, like TLS */ private static final String DEFAULT_PROTOCOL = "TLS"; /** The security mode. True - Implicit Mode / False - Explicit Mode. */ private final boolean isImplicit; /** The secure socket protocol to be used, like SSL/TLS. */ private final String protocol; /** The context object. */ private SSLContext context; /** * The cipher suites. SSLSockets have a default set of these anyway, so no initialization required. */ private String[] suites; /** The protocol versions. */ private String[] protocols; /** The {@link TrustManager} implementation, default null (i.e. use system managers). */ private TrustManager trustManager; /** The {@link KeyManager}, default null (i.e. use system managers). */ private KeyManager keyManager; // seems not to be required /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ private HostnameVerifier hostnameVerifier; /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */ private boolean tlsEndpointChecking; /** * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS Sets security mode to explicit (isImplicit = false). */ public SMTPSClient() { this(DEFAULT_PROTOCOL, false); } /** * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS * * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit */ public SMTPSClient(final boolean implicit) { this(DEFAULT_PROTOCOL, implicit); } /** * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS * * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit * @param ctx A pre-configured SSL Context. */ public SMTPSClient(final boolean implicit, final SSLContext ctx) { isImplicit = implicit; context = ctx; protocol = DEFAULT_PROTOCOL; } /** * Constructor for SMTPSClient. * * @param context A pre-configured SSL Context. * @see #SMTPSClient(boolean, SSLContext) */ public SMTPSClient(final SSLContext context) { this(false, context); } /** * Constructor for SMTPSClient, using explicit security mode. * * @param proto the protocol. */ public SMTPSClient(final String proto) { this(proto, false); } /** * Constructor for SMTPSClient. * * @param proto the protocol. * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit */ public SMTPSClient(final String proto, final boolean implicit) { protocol = proto; isImplicit = implicit; } /** * Constructor for SMTPSClient. * * @param proto the protocol. * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit * @param encoding the encoding * @since 3.3 */ public SMTPSClient(final String proto, final boolean implicit, final String encoding) { super(encoding); protocol = proto; isImplicit = implicit; } /** * Because there are so many connect() methods, the _connectAction_() method is provided as a means of performing some action immediately after establishing * a connection, rather than reimplementing all of the connect() methods. * * @throws IOException If it is thrown by _connectAction_(). * @see org.apache.commons.net.SocketClient#_connectAction_() */ @Override protected void _connectAction_() throws IOException { // Implicit mode. if (isImplicit) { applySocketAttributes(); performSSLNegotiation(); } super._connectAction_(); // Explicit mode - don't do anything. The user calls execTLS() } /** * The TLS command execution. * * @throws IOException If an I/O error occurs while sending the command or performing the negotiation. * @return TRUE if the command and negotiation succeeded. */ public boolean execTLS() throws IOException { if (!SMTPReply.isPositiveCompletion(sendCommand("STARTTLS"))) { return false; // throw new SSLException(getReplyString()); } performSSLNegotiation(); return true; } /** * Returns the names of the cipher suites which could be enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is not an * {@link SSLSocket} instance, returns null. * * @return An array of cipher suite names, or null. */ public String[] getEnabledCipherSuites() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledCipherSuites(); } return null; } /** * Returns the names of the protocol versions which are currently enabled for use on this connection. When the underlying {@link java.net.Socket Socket} is * not an {@link SSLSocket} instance, returns null. * * @return An array of protocols, or null. */ public String[] getEnabledProtocols() { if (_socket_ instanceof SSLSocket) { return ((SSLSocket) _socket_).getEnabledProtocols(); } return null; } /** * Get the currently configured {@link HostnameVerifier}. * * @return A HostnameVerifier instance. * @since 3.4 */ public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } /** * Get the {@link KeyManager} instance. * * @return The current {@link KeyManager} instance. */ public KeyManager getKeyManager() { return keyManager; } /** * Get the currently configured {@link TrustManager}. * * @return A TrustManager instance. */ public TrustManager getTrustManager() { return trustManager; } /** * Performs a lazy init of the SSL context. * * @throws IOException When could not initialize the SSL context. */ private void initSSLContext() throws IOException { if (context == null) { context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); } } /** * Return whether or not endpoint identification using the HTTPS algorithm on Java 1.7+ is enabled. The default behavior is for this to be disabled. * * @return True if enabled, false if not. * @since 3.4 */ public boolean isEndpointCheckingEnabled() { return tlsEndpointChecking; } /** * SSL/TLS negotiation. Acquires an SSL socket of a connection and carries out handshake processing. * * @throws IOException If server negotiation fails. */ private void performSSLNegotiation() throws IOException { initSSLContext(); final SSLSocketFactory ssf = context.getSocketFactory(); final String host = _hostname_ != null ? _hostname_ : getRemoteAddress().getHostAddress(); final int port = getRemotePort(); final SSLSocket socket = (SSLSocket) ssf.createSocket(_socket_, host, port, true); socket.setEnableSessionCreation(true); socket.setUseClientMode(true); if (tlsEndpointChecking) { SSLSocketUtils.enableEndpointNameVerification(socket); } if (protocols != null) { socket.setEnabledProtocols(protocols); } if (suites != null) { socket.setEnabledCipherSuites(suites); } socket.startHandshake(); // TODO the following setup appears to duplicate that in the super class methods _socket_ = socket; _input_ = socket.getInputStream(); _output_ = socket.getOutputStream(); reader = new CRLFLineReader(new InputStreamReader(_input_, encoding)); writer = new BufferedWriter(new OutputStreamWriter(_output_, encoding)); if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { throw new SSLHandshakeException("Hostname doesn't match certificate"); } } /** * Controls which particular cipher suites are enabled for use on this connection. Called before server negotiation. * * @param cipherSuites The cipher suites. */ public void setEnabledCipherSuites(final String[] cipherSuites) { suites = cipherSuites.clone(); } /** * Controls which particular protocol versions are enabled for use on this connection. I perform setting before a server negotiation. * * @param protocolVersions The protocol versions. */ public void setEnabledProtocols(final String[] protocolVersions) { protocols = protocolVersions.clone(); } /** * Automatic endpoint identification checking using the HTTPS algorithm is supported on Java 1.7+. The default behavior is for this to be disabled. * * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+. * @since 3.4 */ public void setEndpointCheckingEnabled(final boolean enable) { tlsEndpointChecking = enable; } /** * Override the default {@link HostnameVerifier} to use. * * @param newHostnameVerifier The HostnameVerifier implementation to set or null to disable. * @since 3.4 */ public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier) { hostnameVerifier = newHostnameVerifier; } /** * Set a {@link KeyManager} to use. * * @param newKeyManager The KeyManager implementation to set. * @see org.apache.commons.net.util.KeyManagerUtils */ public void setKeyManager(final KeyManager newKeyManager) { keyManager = newKeyManager; } /** * Override the default {@link TrustManager} to use. * * @param newTrustManager The TrustManager implementation to set. * @see org.apache.commons.net.util.TrustManagerUtils */ public void setTrustManager(final TrustManager newTrustManager) { trustManager = newTrustManager; } } /* kate: indent-width 4; replace-tabs on; */ commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/SimpleSMTPHeader.java000066400000000000000000000126541434047722200326150ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * This class is used to construct a bare minimum acceptable header for an email message. To construct more complicated headers you should refer to RFC 5322. * When the Java Mail API is finalized, you will be able to use it to compose fully compliant Internet text messages. *

* The main purpose of the class is to faciliatate the mail sending process, by relieving the programmer from having to explicitly format a simple message * header. For example: * *

 * writer = client.sendMessageData();
 * if(writer == null) // failure
 *   return false;
 * header =
 *    new SimpleSMTPHeader("foobar@foo.com", "foo@bar.com" "Just testing");
 * header.addCC("bar@foo.com");
 * header.addHeaderField("Organization", "Foobar, Inc.");
 * writer.write(header.toString());
 * writer.write("This is just a test");
 * writer.close();
 * if(!client.completePendingCommand()) // failure
 *   return false;
 * 
* * @see SMTPClient */ public class SimpleSMTPHeader { private final String subject; private final String from; private final String to; private final StringBuffer headerFields; private boolean hasHeaderDate; private StringBuffer cc; /** * Creates a new SimpleSMTPHeader instance initialized with the given from, to, and subject header field values. *

* * @param from The value of the From: header field. This should be the sender's email address. Must not be null. * @param to The value of the To: header field. This should be the recipient's email address. May be null * @param subject The value of the Subject: header field. This should be the subject of the message. May be null */ public SimpleSMTPHeader(final String from, final String to, final String subject) { if (from == null) { throw new IllegalArgumentException("From cannot be null"); } this.to = to; this.from = from; this.subject = subject; this.headerFields = new StringBuffer(); this.cc = null; } /** * Add an email address to the CC (carbon copy or courtesy copy) list. *

* * @param address The email address to add to the CC list. */ public void addCC(final String address) { if (cc == null) { cc = new StringBuffer(); } else { cc.append(", "); } cc.append(address); } /** * Adds an arbitrary header field with the given value to the article header. These headers will be written before the From, To, Subject, and Cc fields when * the SimpleSMTPHeader is convertered to a string. An example use would be: * *

     * header.addHeaderField("Organization", "Foobar, Inc.");
     * 
*

* * @param headerField The header field to add, not including the colon. * @param value The value of the added header field. */ public void addHeaderField(final String headerField, final String value) { if (!hasHeaderDate && "Date".equals(headerField)) { hasHeaderDate = true; } headerFields.append(headerField); headerFields.append(": "); headerFields.append(value); headerFields.append('\n'); } /** * Converts the SimpleSMTPHeader to a properly formatted header in the form of a String, including the blank line used to separate the header from the * article body. The header fields CC and Subject are only included when they are non-null. *

* * @return The message header in the form of a String. */ @Override public String toString() { final StringBuilder header = new StringBuilder(); final String pattern = "EEE, dd MMM yyyy HH:mm:ss Z"; // Fri, 21 Nov 1997 09:55:06 -0600 final SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.ENGLISH); if (!hasHeaderDate) { addHeaderField("Date", format.format(new Date())); } if (headerFields.length() > 0) { header.append(headerFields.toString()); } header.append("From: ").append(from).append("\n"); if (to != null) { header.append("To: ").append(to).append("\n"); } if (cc != null) { header.append("Cc: ").append(cc.toString()).append("\n"); } if (subject != null) { header.append("Subject: ").append(subject).append("\n"); } header.append('\n'); // end of headers; body follows return header.toString(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/smtp/package-info.java000066400000000000000000000015471434047722200320720ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * SMTP and SMTPS mail */ package org.apache.commons.net.smtp;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/000077500000000000000000000000001434047722200272045ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/EchoOptionHandler.java000066400000000000000000000036461434047722200334250ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * Implements the telnet echo option RFC 857. */ public class EchoOptionHandler extends TelnetOptionHandler { /** * Constructor for the EchoOptionHandler. Initial and accept behavior flags are set to false */ public EchoOptionHandler() { super(TelnetOption.ECHO, false, false, false, false); } /** * Constructor for the EchoOptionHandler. Allows defining desired initial setting for local/remote activation of this option and behavior in case a * local/remote activation request for this option is received. *

* * @param initlocal - if set to true, a WILL is sent upon connection. * @param initremote - if set to true, a DO is sent upon connection. * @param acceptlocal - if set to true, any DO request is accepted. * @param acceptremote - if set to true, any WILL request is accepted. */ public EchoOptionHandler(final boolean initlocal, final boolean initremote, final boolean acceptlocal, final boolean acceptremote) { super(TelnetOption.ECHO, initlocal, initremote, acceptlocal, acceptremote); } } InvalidTelnetOptionException.java000066400000000000000000000034231434047722200356040ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * The InvalidTelnetOptionException is the exception that is thrown whenever a TelnetOptionHandler with an invlaid option code is registered in TelnetClient * with addOptionHandler. */ public class InvalidTelnetOptionException extends Exception { private static final long serialVersionUID = -2516777155928793597L; /** * Option code */ private final int optionCode; /** * Error message */ private final String msg; /** * Constructor for the exception. *

* * @param message - Error message. * @param optcode - Option code. */ public InvalidTelnetOptionException(final String message, final int optcode) { optionCode = optcode; msg = message; } /** * Gets the error message of ths exception. *

* * @return the error message. */ @Override public String getMessage() { return msg + ": " + optionCode; } } SimpleOptionHandler.java000066400000000000000000000041141434047722200337100ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * Simple option handler that can be used for options that don't require subnegotiation. */ public class SimpleOptionHandler extends TelnetOptionHandler { /** * Constructor for the SimpleOptionHandler. Initial and accept behavior flags are set to false *

* * @param optcode - option code. */ public SimpleOptionHandler(final int optcode) { super(optcode, false, false, false, false); } /** * Constructor for the SimpleOptionHandler. Allows defining desired initial setting for local/remote activation of this option and behavior in case a * local/remote activation request for this option is received. *

* * @param optcode - option code. * @param initlocal - if set to true, a WILL is sent upon connection. * @param initremote - if set to true, a DO is sent upon connection. * @param acceptlocal - if set to true, any DO request is accepted. * @param acceptremote - if set to true, any WILL request is accepted. */ public SimpleOptionHandler(final int optcode, final boolean initlocal, final boolean initremote, final boolean acceptlocal, final boolean acceptremote) { super(optcode, initlocal, initremote, acceptlocal, acceptremote); } } SuppressGAOptionHandler.java000066400000000000000000000037531434047722200345230ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * Implements the telnet suppress go ahead option RFC 858. */ public class SuppressGAOptionHandler extends TelnetOptionHandler { /** * Constructor for the SuppressGAOptionHandler. Initial and accept behavior flags are set to false */ public SuppressGAOptionHandler() { super(TelnetOption.SUPPRESS_GO_AHEAD, false, false, false, false); } /** * Constructor for the SuppressGAOptionHandler. Allows defining desired initial setting for local/remote activation of this option and behavior in case a * local/remote activation request for this option is received. *

* * @param initlocal - if set to true, a WILL is sent upon connection. * @param initremote - if set to true, a DO is sent upon connection. * @param acceptlocal - if set to true, any DO request is accepted. * @param acceptremote - if set to true, any WILL request is accepted. */ public SuppressGAOptionHandler(final boolean initlocal, final boolean initremote, final boolean acceptlocal, final boolean acceptremote) { super(TelnetOption.SUPPRESS_GO_AHEAD, initlocal, initremote, acceptlocal, acceptremote); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/Telnet.java000066400000000000000000000735221434047722200313130ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import org.apache.commons.net.SocketClient; class Telnet extends SocketClient { static final boolean debug = /* true; */ false; static final boolean debugoptions = /* true; */ false; static final byte[] COMMAND_DO = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO }; static final byte[] COMMAND_DONT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT }; static final byte[] COMMAND_WILL = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL }; static final byte[] COMMAND_WONT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT }; static final byte[] COMMAND_SB = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB }; static final byte[] COMMAND_SE = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE }; static final int WILL_MASK = 0x01; static final int DO_MASK = 0x02; static final int REQUESTED_WILL_MASK = 0x04; static final int REQUESTED_DO_MASK = 0x08; /* public */ static final int DEFAULT_PORT = 23; /* TERMINAL-TYPE option (start) */ /** * Terminal type option */ protected static final int TERMINAL_TYPE = 24; /** * Send (for subnegotiation) */ protected static final int TERMINAL_TYPE_SEND = 1; /** * Is (for subnegotiation) */ protected static final int TERMINAL_TYPE_IS = 0; /** * Is sequence (for subnegotiation) */ static final byte[] COMMAND_IS = { (byte) TERMINAL_TYPE, (byte) TERMINAL_TYPE_IS }; /* Code Section added for supporting AYT (start) */ /** * AYT sequence */ static final byte[] COMMAND_AYT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.AYT }; private final int[] doResponse; private final int[] willResponse; private final int[] options; /** * Terminal type */ private String terminalType; /* TERMINAL-TYPE option (end) */ /* open TelnetOptionHandler functionality (end) */ /* open TelnetOptionHandler functionality (start) */ /** * Array of option handlers */ private final TelnetOptionHandler[] optionHandlers; /** * monitor to wait for AYT */ private final Object aytMonitor = new Object(); /** * flag for AYT */ private volatile boolean aytFlag = true; /* Code Section added for supporting AYT (end) */ /** * The stream on which to spy */ private volatile OutputStream spyStream; /** * The notification handler */ private TelnetNotificationHandler notifhand; /** * Empty Constructor */ Telnet() { setDefaultPort(DEFAULT_PORT); doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1]; willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1]; options = new int[TelnetOption.MAX_OPTION_VALUE + 1]; optionHandlers = new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1]; } /* TERMINAL-TYPE option (start) */ /** * This constructor lets you specify the terminal type. * * @param termtype - terminal type to be negotiated (ej. VT100) */ Telnet(final String termtype) { setDefaultPort(DEFAULT_PORT); doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1]; willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1]; options = new int[TelnetOption.MAX_OPTION_VALUE + 1]; terminalType = termtype; optionHandlers = new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1]; } /* TERMINAL-TYPE option (end) */ /** * Called upon connection. * * @throws IOException - Exception in I/O. */ @Override protected void _connectAction_() throws IOException { /* (start). BUGFIX: clean the option info for each connection */ for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++) { doResponse[ii] = 0; willResponse[ii] = 0; options[ii] = 0; if (optionHandlers[ii] != null) { optionHandlers[ii].setDo(false); optionHandlers[ii].setWill(false); } } /* (end). BUGFIX: clean the option info for each connection */ super._connectAction_(); _input_ = new BufferedInputStream(_input_); _output_ = new BufferedOutputStream(_output_); /* open TelnetOptionHandler functionality (start) */ for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++) { if (optionHandlers[ii] != null) { if (optionHandlers[ii].getInitLocal()) { requestWill(optionHandlers[ii].getOptionCode()); } if (optionHandlers[ii].getInitRemote()) { requestDo(optionHandlers[ii].getOptionCode()); } } } /* open TelnetOptionHandler functionality (end) */ } /* Code Section added for supporting spystreams (start) */ /** * Registers an OutputStream for spying what's going on in the Telnet session. * * @param spystream - OutputStream on which session activity will be echoed. */ void _registerSpyStream(final OutputStream spystream) { spyStream = spystream; } /* Code Section added for supporting AYT (start) */ /** * Sends an Are You There sequence and waits for the result. * * @param timeout - Time to wait for a response (millis.) * @throws IOException - Exception in I/O. * @throws IllegalArgumentException - Illegal argument * @throws InterruptedException - Interrupted during wait. * @return true if AYT received a response, false otherwise **/ final boolean _sendAYT(final long timeout) throws IOException, IllegalArgumentException, InterruptedException { boolean retValue = false; synchronized (aytMonitor) { synchronized (this) { aytFlag = false; _output_.write(COMMAND_AYT); _output_.flush(); } aytMonitor.wait(timeout); if (!aytFlag) { aytFlag = true; } else { retValue = true; } } return retValue; } /* Code Section added for supporting AYT (end) */ /** * Sends a command, automatically adds IAC prefix and flushes the output. * * @param cmd - command data to be sent * @throws IOException - Exception in I/O. * @since 3.0 */ final synchronized void _sendCommand(final byte cmd) throws IOException { _output_.write(TelnetCommand.IAC); _output_.write(cmd); _output_.flush(); } /* open TelnetOptionHandler functionality (start) */ /** * Manages subnegotiation for Terminal Type. * * @param subn - subnegotiation data to be sent * @throws IOException - Exception in I/O. **/ final synchronized void _sendSubnegotiation(final int[] subn) throws IOException { if (debug) { System.err.println("SEND SUBNEGOTIATION: "); if (subn != null) { System.err.println(Arrays.toString(subn)); } } if (subn != null) { _output_.write(COMMAND_SB); // Note _output_ is buffered, so might as well simplify by writing single bytes for (final int element : subn) { final byte b = (byte) element; if (b == (byte) TelnetCommand.IAC) { // cast is necessary because IAC is outside the signed byte range _output_.write(b); // double any IAC bytes } _output_.write(b); } _output_.write(COMMAND_SE); /* Code Section added for sending the negotiation ASAP (start) */ _output_.flush(); /* Code Section added for sending the negotiation ASAP (end) */ } } /* open TelnetOptionHandler functionality (end) */ /** * Stops spying this Telnet. * */ void _stopSpyStream() { spyStream = null; } /** * Registers a new TelnetOptionHandler for this telnet to use. * * @param opthand - option handler to be registered. * @throws InvalidTelnetOptionException - The option code is invalid. * @throws IOException on error **/ void addOptionHandler(final TelnetOptionHandler opthand) throws InvalidTelnetOptionException, IOException { final int optcode = opthand.getOptionCode(); if (!TelnetOption.isValidOption(optcode)) { throw new InvalidTelnetOptionException("Invalid Option Code", optcode); } if (optionHandlers[optcode] != null) { throw new InvalidTelnetOptionException("Already registered option", optcode); } optionHandlers[optcode] = opthand; if (isConnected()) { if (opthand.getInitLocal()) { requestWill(optcode); } if (opthand.getInitRemote()) { requestDo(optcode); } } } /** * Unregisters a TelnetOptionHandler. * * @param optcode - Code of the option to be unregistered. * @throws InvalidTelnetOptionException - The option code is invalid. * @throws IOException on error **/ void deleteOptionHandler(final int optcode) throws InvalidTelnetOptionException, IOException { if (!TelnetOption.isValidOption(optcode)) { throw new InvalidTelnetOptionException("Invalid Option Code", optcode); } if (optionHandlers[optcode] == null) { throw new InvalidTelnetOptionException("Unregistered option", optcode); } final TelnetOptionHandler opthand = optionHandlers[optcode]; optionHandlers[optcode] = null; if (opthand.getWill()) { requestWont(optcode); } if (opthand.getDo()) { requestDont(optcode); } } /* open TelnetOptionHandler functionality (end) */ /* Code Section added for supporting AYT (start) */ /** * Processes the response of an AYT */ final synchronized void processAYTResponse() { if (!aytFlag) { synchronized (aytMonitor) { aytFlag = true; aytMonitor.notifyAll(); } } } /* Code Section added for supporting AYT (end) */ /** * Processes a COMMAND. * * @param command - option code to be set. **/ void processCommand(final int command) { if (debugoptions) { System.err.println("RECEIVED COMMAND: " + command); } if (notifhand != null) { notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_COMMAND, command); } } /** * Processes a DO request. * * @param option - option code to be set. * @throws IOException - Exception in I/O. **/ void processDo(final int option) throws IOException { if (debugoptions) { System.err.println("RECEIVED DO: " + TelnetOption.getOption(option)); } if (notifhand != null) { notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DO, option); } boolean acceptNewState = false; /* open TelnetOptionHandler functionality (start) */ if (optionHandlers[option] != null) { acceptNewState = optionHandlers[option].getAcceptLocal(); } else if (option == TERMINAL_TYPE && terminalType != null && !terminalType.isEmpty()) { acceptNewState = true; } /* TERMINAL-TYPE option (end) */ /* open TelnetOptionHandler functionality (start) */ if (willResponse[option] > 0) { --willResponse[option]; if (willResponse[option] > 0 && stateIsWill(option)) { --willResponse[option]; } } if (willResponse[option] == 0) { if (requestedWont(option)) { switch (option) { default: break; } if (acceptNewState) { setWantWill(option); sendWill(option); } else { ++willResponse[option]; sendWont(option); } } else { // Other end has acknowledged option. switch (option) { default: break; } } } setWill(option); } /** * Processes a DONT request. * * @param option - option code to be set. * @throws IOException - Exception in I/O. **/ void processDont(final int option) throws IOException { if (debugoptions) { System.err.println("RECEIVED DONT: " + TelnetOption.getOption(option)); } if (notifhand != null) { notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DONT, option); } if (willResponse[option] > 0) { --willResponse[option]; if (willResponse[option] > 0 && stateIsWont(option)) { --willResponse[option]; } } if (willResponse[option] == 0 && requestedWill(option)) { switch (option) { default: break; } /* FIX for a BUG in the negotiation (start) */ if (stateIsWill(option) || requestedWill(option)) { sendWont(option); } setWantWont(option); /* FIX for a BUG in the negotiation (end) */ } setWont(option); } /* TERMINAL-TYPE option (start) */ /** * Processes a suboption negotiation. * * @param suboption - subnegotiation data received * @param suboptionLength - length of data received * @throws IOException - Exception in I/O. **/ void processSuboption(final int[] suboption, final int suboptionLength) throws IOException { if (debug) { System.err.println("PROCESS SUBOPTION."); } /* open TelnetOptionHandler functionality (start) */ if (suboptionLength > 0) { if (optionHandlers[suboption[0]] != null) { final int[] responseSuboption = optionHandlers[suboption[0]].answerSubnegotiation(suboption, suboptionLength); _sendSubnegotiation(responseSuboption); } else if (suboptionLength > 1) { if (debug) { for (int ii = 0; ii < suboptionLength; ii++) { System.err.println("SUB[" + ii + "]: " + suboption[ii]); } } if (suboption[0] == TERMINAL_TYPE && suboption[1] == TERMINAL_TYPE_SEND) { sendTerminalType(); } } } /* open TelnetOptionHandler functionality (end) */ } /** * Processes a WILL request. * * @param option - option code to be set. * @throws IOException - Exception in I/O. **/ void processWill(final int option) throws IOException { if (debugoptions) { System.err.println("RECEIVED WILL: " + TelnetOption.getOption(option)); } if (notifhand != null) { notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WILL, option); } boolean acceptNewState = false; /* open TelnetOptionHandler functionality (start) */ if (optionHandlers[option] != null) { acceptNewState = optionHandlers[option].getAcceptRemote(); } /* open TelnetOptionHandler functionality (end) */ if (doResponse[option] > 0) { --doResponse[option]; if (doResponse[option] > 0 && stateIsDo(option)) { --doResponse[option]; } } if (doResponse[option] == 0 && requestedDont(option)) { switch (option) { default: break; } if (acceptNewState) { setWantDo(option); sendDo(option); } else { ++doResponse[option]; sendDont(option); } } setDo(option); } /** * Processes a WONT request. * * @param option - option code to be set. * @throws IOException - Exception in I/O. **/ void processWont(final int option) throws IOException { if (debugoptions) { System.err.println("RECEIVED WONT: " + TelnetOption.getOption(option)); } if (notifhand != null) { notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WONT, option); } if (doResponse[option] > 0) { --doResponse[option]; if (doResponse[option] > 0 && stateIsDont(option)) { --doResponse[option]; } } if (doResponse[option] == 0 && requestedDo(option)) { switch (option) { default: break; } /* FIX for a BUG in the negotiation (start) */ if (stateIsDo(option) || requestedDo(option)) { sendDont(option); } setWantDont(option); /* FIX for a BUG in the negotiation (end) */ } setDont(option); } /** * Registers a notification handler to which will be sent notifications of received telnet option negotiation commands. * * @param notifhand - TelnetNotificationHandler to be registered */ public void registerNotifHandler(final TelnetNotificationHandler notifhand) { this.notifhand = notifhand; } /** * Requests a DO. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void requestDo(final int option) throws IOException { if (doResponse[option] == 0 && stateIsDo(option) || requestedDo(option)) { return; } setWantDo(option); ++doResponse[option]; sendDo(option); } /** * Requests a DONT. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void requestDont(final int option) throws IOException { if (doResponse[option] == 0 && stateIsDont(option) || requestedDont(option)) { return; } setWantDont(option); ++doResponse[option]; sendDont(option); } /** * Looks for the state of the option. * * @return returns true if a do has been reuqested * * @param option - option code to be looked up. */ boolean requestedDo(final int option) { return (options[option] & REQUESTED_DO_MASK) != 0; } /** * Looks for the state of the option. * * @return returns true if a dont has been reuqested * * @param option - option code to be looked up. */ boolean requestedDont(final int option) { return !requestedDo(option); } /** * Looks for the state of the option. * * @return returns true if a will has been reuqested * * @param option - option code to be looked up. */ boolean requestedWill(final int option) { return (options[option] & REQUESTED_WILL_MASK) != 0; } /** * Looks for the state of the option. * * @return returns true if a wont has been reuqested * * @param option - option code to be looked up. */ boolean requestedWont(final int option) { return !requestedWill(option); } /** * Requests a WILL. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void requestWill(final int option) throws IOException { if (willResponse[option] == 0 && stateIsWill(option) || requestedWill(option)) { return; } setWantWill(option); ++doResponse[option]; sendWill(option); } /* TERMINAL-TYPE option (end) */ /** * Requests a WONT. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void requestWont(final int option) throws IOException { if (willResponse[option] == 0 && stateIsWont(option) || requestedWont(option)) { return; } setWantWont(option); ++doResponse[option]; sendWont(option); } /** * Sends a byte. * * @param b - byte to send * @throws IOException - Exception in I/O. **/ final synchronized void sendByte(final int b) throws IOException { _output_.write(b); /* Code Section added for supporting spystreams (start) */ spyWrite(b); /* Code Section added for supporting spystreams (end) */ } /** * Sends a DO. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void sendDo(final int option) throws IOException { if (debug || debugoptions) { System.err.println("DO: " + TelnetOption.getOption(option)); } _output_.write(COMMAND_DO); _output_.write(option); /* Code Section added for sending the negotiation ASAP (start) */ _output_.flush(); /* Code Section added for sending the negotiation ASAP (end) */ } /** * Sends a DONT. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void sendDont(final int option) throws IOException { if (debug || debugoptions) { System.err.println("DONT: " + TelnetOption.getOption(option)); } _output_.write(COMMAND_DONT); _output_.write(option); /* Code Section added for sending the negotiation ASAP (start) */ _output_.flush(); /* Code Section added for sending the negotiation ASAP (end) */ } /** * Sends terminal type information. * * @throws IOException - Exception in I/O. */ final synchronized void sendTerminalType() throws IOException { if (debug) { System.err.println("SEND TERMINAL-TYPE: " + terminalType); } if (terminalType != null) { _output_.write(COMMAND_SB); _output_.write(COMMAND_IS); _output_.write(terminalType.getBytes(getCharset())); _output_.write(COMMAND_SE); _output_.flush(); } } /** * Sends a WILL. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void sendWill(final int option) throws IOException { if (debug || debugoptions) { System.err.println("WILL: " + TelnetOption.getOption(option)); } _output_.write(COMMAND_WILL); _output_.write(option); /* Code Section added for sending the negotiation ASAP (start) */ _output_.flush(); /* Code Section added for sending the negotiation ASAP (end) */ } /** * Sends a WONT. * * @param option - Option code. * @throws IOException - Exception in I/O. **/ final synchronized void sendWont(final int option) throws IOException { if (debug || debugoptions) { System.err.println("WONT: " + TelnetOption.getOption(option)); } _output_.write(COMMAND_WONT); _output_.write(option); /* Code Section added for sending the negotiation ASAP (start) */ _output_.flush(); /* Code Section added for sending the negotiation ASAP (end) */ } /** * Sets the state of the option. * * @param option - option code to be set. * @throws IOException */ void setDo(final int option) throws IOException { options[option] |= DO_MASK; /* open TelnetOptionHandler functionality (start) */ if (requestedDo(option) && (optionHandlers[option] != null)) { optionHandlers[option].setDo(true); final int[] subneg = optionHandlers[option].startSubnegotiationRemote(); if (subneg != null) { _sendSubnegotiation(subneg); } } /* open TelnetOptionHandler functionality (end) */ } /** * Sets the state of the option. * * @param option - option code to be set. */ void setDont(final int option) { options[option] &= ~DO_MASK; /* open TelnetOptionHandler functionality (start) */ if (optionHandlers[option] != null) { optionHandlers[option].setDo(false); } /* open TelnetOptionHandler functionality (end) */ } /** * Sets the state of the option. * * @param option - option code to be set. */ void setWantDo(final int option) { options[option] |= REQUESTED_DO_MASK; } /** * Sets the state of the option. * * @param option - option code to be set. */ void setWantDont(final int option) { options[option] &= ~REQUESTED_DO_MASK; } /** * Sets the state of the option. * * @param option - option code to be set. */ void setWantWill(final int option) { options[option] |= REQUESTED_WILL_MASK; } /** * Sets the state of the option. * * @param option - option code to be set. */ void setWantWont(final int option) { options[option] &= ~REQUESTED_WILL_MASK; } /** * Sets the state of the option. * * @param option - option code to be set. * @throws IOException */ void setWill(final int option) throws IOException { options[option] |= WILL_MASK; /* open TelnetOptionHandler functionality (start) */ if (requestedWill(option) && (optionHandlers[option] != null)) { optionHandlers[option].setWill(true); final int[] subneg = optionHandlers[option].startSubnegotiationLocal(); if (subneg != null) { _sendSubnegotiation(subneg); } } /* open TelnetOptionHandler functionality (end) */ } /* open TelnetOptionHandler functionality (start) */ /** * Sets the state of the option. * * @param option - option code to be set. */ void setWont(final int option) { options[option] &= ~WILL_MASK; /* open TelnetOptionHandler functionality (start) */ if (optionHandlers[option] != null) { optionHandlers[option].setWill(false); } /* open TelnetOptionHandler functionality (end) */ } /** * Sends a read char on the spy stream. * * @param ch - character read from the session */ void spyRead(final int ch) { final OutputStream spy = spyStream; if (spy != null) { try { if (ch != '\r') // never write '\r' on its own { if (ch == '\n') { spy.write('\r'); // add '\r' before '\n' } spy.write(ch); // write original character spy.flush(); } } catch (final IOException e) { spyStream = null; } } } /** * Sends a written char on the spy stream. * * @param ch - character written to the session */ void spyWrite(final int ch) { if (!(stateIsDo(TelnetOption.ECHO) && requestedDo(TelnetOption.ECHO))) { final OutputStream spy = spyStream; if (spy != null) { try { spy.write(ch); spy.flush(); } catch (final IOException e) { spyStream = null; } } } } /* Code Section added for supporting spystreams (end) */ /** * Looks for the state of the option. * * @return returns true if a do has been acknowledged * * @param option - option code to be looked up. */ boolean stateIsDo(final int option) { return (options[option] & DO_MASK) != 0; } /** * Looks for the state of the option. * * @return returns true if a dont has been acknowledged * * @param option - option code to be looked up. */ boolean stateIsDont(final int option) { return !stateIsDo(option); } /** * Looks for the state of the option. * * @return returns true if a will has been acknowledged * * @param option - option code to be looked up. */ boolean stateIsWill(final int option) { return (options[option] & WILL_MASK) != 0; } /** * Looks for the state of the option. * * @return returns true if a wont has been acknowledged * * @param option - option code to be looked up. */ boolean stateIsWont(final int option) { return !stateIsWill(option); } /** * Unregisters the current notification handler. * */ public void unregisterNotifHandler() { this.notifhand = null; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/TelnetClient.java000066400000000000000000000334301434047722200324440ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * The TelnetClient class implements the simple network virtual terminal (NVT) for the Telnet protocol according to RFC 854. It does not implement any of the * extra Telnet options because it is meant to be used within a Java program providing automated access to Telnet accessible resources. *

* The class can be used by first connecting to a server using the SocketClient {@link org.apache.commons.net.SocketClient#connect connect} method. Then an * InputStream and OutputStream for sending and receiving data over the Telnet connection can be obtained by using the {@link #getInputStream getInputStream() } * and {@link #getOutputStream getOutputStream() } methods. When you finish using the streams, you must call {@link #disconnect disconnect } rather than simply * closing the streams. */ public class TelnetClient extends Telnet { private static final int DEFAULT_MAX_SUBNEGOTIATION_LENGTH = 512; final int maxSubnegotiationLength; private InputStream input; private OutputStream output; protected boolean readerThread = true; private TelnetInputListener inputListener; /** * Default TelnetClient constructor, sets terminal-type {@code VT100}. */ public TelnetClient() { this("VT100", DEFAULT_MAX_SUBNEGOTIATION_LENGTH); } /** * Construct an instance with the specified max subnegotiation length and the default terminal-type {@code VT100} * * @param maxSubnegotiationLength the size of the subnegotiation buffer */ public TelnetClient(final int maxSubnegotiationLength) { this("VT100", maxSubnegotiationLength); } /** * Construct an instance with the specified terminal type. * * @param termtype the terminal type to use, e.g. {@code VT100} */ public TelnetClient(final String termtype) { this(termtype, DEFAULT_MAX_SUBNEGOTIATION_LENGTH); } /** * Construct an instance with the specified terminal type and max subnegotiation length * * @param termtype the terminal type to use, e.g. {@code VT100} * @param maxSubnegotiationLength the size of the subnegotiation buffer */ public TelnetClient(final String termtype, final int maxSubnegotiationLength) { /* TERMINAL-TYPE option (start) */ super(termtype); /* TERMINAL-TYPE option (end) */ this.input = null; this.output = null; this.maxSubnegotiationLength = maxSubnegotiationLength; } /** * Handles special connection requirements. * * @throws IOException If an error occurs during connection setup. */ @Override protected void _connectAction_() throws IOException { super._connectAction_(); final TelnetInputStream tmp = new TelnetInputStream(_input_, this, readerThread); if (readerThread) { tmp.start(); } // __input CANNOT refer to the TelnetInputStream. We run into // blocking problems when some classes use TelnetInputStream, so // we wrap it with a BufferedInputStream which we know is safe. // This blocking behavior requires further investigation, but right // now it looks like classes like InputStreamReader are not implemented // in a safe manner. input = new BufferedInputStream(tmp); output = new TelnetOutputStream(this); } /** * Registers a new TelnetOptionHandler for this telnet client to use. * * @param opthand - option handler to be registered. * * @throws InvalidTelnetOptionException on error * @throws IOException on error */ @Override public void addOptionHandler(final TelnetOptionHandler opthand) throws InvalidTelnetOptionException, IOException { super.addOptionHandler(opthand); } /* open TelnetOptionHandler functionality (end) */ void closeOutputStream() throws IOException { if (_output_ == null) { return; } try { _output_.close(); } finally { _output_ = null; } } /** * Unregisters a TelnetOptionHandler. * * @param optcode - Code of the option to be unregistered. * * @throws InvalidTelnetOptionException on error * @throws IOException on error */ @Override public void deleteOptionHandler(final int optcode) throws InvalidTelnetOptionException, IOException { super.deleteOptionHandler(optcode); } /** * Disconnects the telnet session, closing the input and output streams as well as the socket. If you have references to the input and output streams of the * telnet connection, you should not close them yourself, but rather call disconnect to properly close the connection. */ @Override public void disconnect() throws IOException { try { if (input != null) { input.close(); } if (output != null) { output.close(); } } finally { // NET-594 output = null; input = null; super.disconnect(); } } void flushOutputStream() throws IOException { if (_output_ == null) { throw new IOException("Stream closed"); } _output_.flush(); } /** * Returns the telnet connection input stream. You should not close the stream when you finish with it. Rather, you should call {@link #disconnect * disconnect }. * * @return The telnet connection input stream. */ public InputStream getInputStream() { return input; } /** * Returns the state of the option on the local side. * * @param option - Option to be checked. * * @return The state of the option on the local side. */ public boolean getLocalOptionState(final int option) { /* BUG (option active when not already acknowledged) (start) */ return stateIsWill(option) && requestedWill(option); /* BUG (option active when not already acknowledged) (end) */ } /* Code Section added for supporting AYT (start) */ /** * Returns the telnet connection output stream. You should not close the stream when you finish with it. Rather, you should call {@link #disconnect * disconnect }. * * @return The telnet connection output stream. */ public OutputStream getOutputStream() { return output; } /** * Gets the status of the reader thread. * * @return true if the reader thread is enabled, false otherwise */ public boolean getReaderThread() { return readerThread; } /** * Returns the state of the option on the remote side. * * @param option - Option to be checked. * * @return The state of the option on the remote side. */ public boolean getRemoteOptionState(final int option) { /* BUG (option active when not already acknowledged) (start) */ return stateIsDo(option) && requestedDo(option); /* BUG (option active when not already acknowledged) (end) */ } /* open TelnetOptionHandler functionality (end) */ /* open TelnetOptionHandler functionality (start) */ // Notify input listener void notifyInputListener() { final TelnetInputListener listener; synchronized (this) { listener = this.inputListener; } if (listener != null) { listener.telnetInputAvailable(); } } /** * Register a listener to be notified when new incoming data is available to be read on the {@link #getInputStream input stream}. Only one listener is * supported at a time. * *

* More precisely, notifications are issued whenever the number of bytes available for immediate reading (i.e., the value returned by * {@link InputStream#available}) transitions from zero to non-zero. Note that (in general) multiple reads may be required to empty the buffer and reset * this notification, because incoming bytes are being added to the internal buffer asynchronously. *

* *

* Notifications are only supported when a {@link #setReaderThread reader thread} is enabled for the connection. *

* * @param listener listener to be registered; replaces any previous * @since 3.0 */ public synchronized void registerInputListener(final TelnetInputListener listener) { this.inputListener = listener; } /** * Registers a notification handler to which will be sent notifications of received telnet option negotiation commands. * * @param notifhand - TelnetNotificationHandler to be registered */ @Override public void registerNotifHandler(final TelnetNotificationHandler notifhand) { super.registerNotifHandler(notifhand); } /* Code Section added for supporting spystreams (start) */ /** * Registers an OutputStream for spying what's going on in the TelnetClient session. * * @param spystream - OutputStream on which session activity will be echoed. */ public void registerSpyStream(final OutputStream spystream) { super._registerSpyStream(spystream); } /** * Sends an Are You There sequence and waits for the result. * * @param timeout - Time to wait for a response (millis.) * * @return true if AYT received a response, false otherwise * * @throws InterruptedException on error * @throws IllegalArgumentException on error * @throws IOException on error */ public boolean sendAYT(final long timeout) throws IOException, IllegalArgumentException, InterruptedException { return _sendAYT(timeout); } /* Code Section added for supporting AYT (start) */ /** * Sends a command byte to the remote peer, adding the IAC prefix. * *

* This method does not wait for any response. Messages sent by the remote end can be handled by registering an approrpriate {@link TelnetOptionHandler}. *

* * @param command the code for the command * @throws IOException if an I/O error occurs while writing the message * @throws IllegalArgumentException on error * @since 3.0 */ public void sendCommand(final byte command) throws IOException, IllegalArgumentException { _sendCommand(command); } /** * Sends a protocol-specific subnegotiation message to the remote peer. {@link TelnetClient} will add the IAC SB & IAC SE framing bytes; the first byte * in {@code message} should be the appropriate telnet option code. * *

* This method does not wait for any response. Subnegotiation messages sent by the remote end can be handled by registering an approrpriate * {@link TelnetOptionHandler}. *

* * @param message option code followed by subnegotiation payload * @throws IllegalArgumentException if {@code message} has length zero * @throws IOException if an I/O error occurs while writing the message * @since 3.0 */ public void sendSubnegotiation(final int[] message) throws IOException, IllegalArgumentException { if (message.length < 1) { throw new IllegalArgumentException("zero length message"); } _sendSubnegotiation(message); } /** * Sets the status of the reader thread. * *

* When enabled, a seaparate internal reader thread is created for new connections to read incoming data as it arrives. This results in immediate handling * of option negotiation, notifications, etc. (at least until the fixed-size internal buffer fills up). Otherwise, no thread is created an all negotiation * and option handling is deferred until a read() is performed on the {@link #getInputStream input stream}. *

* *

* The reader thread must be enabled for {@link TelnetInputListener} support. *

* *

* When this method is invoked, the reader thread status will apply to all subsequent connections; the current connection (if any) is not affected. *

* * @param flag true to enable the reader thread, false to disable * @see #registerInputListener */ public void setReaderThread(final boolean flag) { readerThread = flag; } /** * Stops spying this TelnetClient. * */ public void stopSpyStream() { super._stopSpyStream(); } /* Code Section added for supporting spystreams (end) */ /** * Unregisters the current {@link TelnetInputListener}, if any. * * @since 3.0 */ public synchronized void unregisterInputListener() { this.inputListener = null; } /** * Unregisters the current notification handler. * */ @Override public void unregisterNotifHandler() { super.unregisterNotifHandler(); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/TelnetCommand.java000066400000000000000000000107051434047722200326040ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * The TelnetCommand class cannot be instantiated and only serves as a storehouse for telnet command constants. * * @see org.apache.commons.net.telnet.Telnet * @see org.apache.commons.net.telnet.TelnetClient */ public final class TelnetCommand { /** The maximum value a command code can have. This value is 255. */ public static final int MAX_COMMAND_VALUE = 255; /** Interpret As Command code. Value is 255 according to RFC 854. */ public static final int IAC = 255; /** Don't use option code. Value is 254 according to RFC 854. */ public static final int DONT = 254; /** Request to use option code. Value is 253 according to RFC 854. */ public static final int DO = 253; /** Refuse to use option code. Value is 252 according to RFC 854. */ public static final int WONT = 252; /** Agree to use option code. Value is 251 according to RFC 854. */ public static final int WILL = 251; /** Start subnegotiation code. Value is 250 according to RFC 854. */ public static final int SB = 250; /** Go Ahead code. Value is 249 according to RFC 854. */ public static final int GA = 249; /** Erase Line code. Value is 248 according to RFC 854. */ public static final int EL = 248; /** Erase Character code. Value is 247 according to RFC 854. */ public static final int EC = 247; /** Are You There code. Value is 246 according to RFC 854. */ public static final int AYT = 246; /** Abort Output code. Value is 245 according to RFC 854. */ public static final int AO = 245; /** Interrupt Process code. Value is 244 according to RFC 854. */ public static final int IP = 244; /** Break code. Value is 243 according to RFC 854. */ public static final int BREAK = 243; /** Data mark code. Value is 242 according to RFC 854. */ public static final int DM = 242; /** No Operation code. Value is 241 according to RFC 854. */ public static final int NOP = 241; /** End subnegotiation code. Value is 240 according to RFC 854. */ public static final int SE = 240; /** End of record code. Value is 239. */ public static final int EOR = 239; /** Abort code. Value is 238. */ public static final int ABORT = 238; /** Suspend process code. Value is 237. */ public static final int SUSP = 237; /** End of file code. Value is 236. */ public static final int EOF = 236; /** Synchronize code. Value is 242. */ public static final int SYNCH = 242; /** String representations of commands. */ private static final String commandString[] = { "IAC", "DONT", "DO", "WONT", "WILL", "SB", "GA", "EL", "EC", "AYT", "AO", "IP", "BRK", "DMARK", "NOP", "SE", "EOR", "ABORT", "SUSP", "EOF" }; private static final int FIRST_COMMAND = IAC; private static final int LAST_COMMAND = EOF; /** * Returns the string representation of the telnet protocol command corresponding to the given command code. *

* * @param code The command code of the telnet protocol command. * @return The string representation of the telnet protocol command. */ public static String getCommand(final int code) { return commandString[FIRST_COMMAND - code]; } /** * Determines if a given command code is valid. Returns true if valid, false if not. *

* * @param code The command code to test. * @return True if the command code is valid, false if not. **/ public static boolean isValidCommand(final int code) { return code <= FIRST_COMMAND && code >= LAST_COMMAND; } // Cannot be instantiated private TelnetCommand() { } } TelnetInputListener.java000066400000000000000000000023641434047722200337560ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * Listener interface used for notification that incoming data is available to be read. * * @see TelnetClient * @since 3.0 */ public interface TelnetInputListener { /** * Callback method invoked when new incoming data is available on a {@link TelnetClient}'s {@link TelnetClient#getInputStream input stream}. * * @see TelnetClient#registerInputListener */ void telnetInputAvailable(); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/TelnetInputStream.java000066400000000000000000000507671434047722200335150ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; final class TelnetInputStream extends BufferedInputStream implements Runnable { /** End of file has been reached */ private static final int EOF = -1; /** Read would block */ private static final int WOULD_BLOCK = -2; // TODO should these be private enums? static final int STATE_DATA = 0, STATE_IAC = 1, STATE_WILL = 2, STATE_WONT = 3, STATE_DO = 4, STATE_DONT = 5, STATE_SB = 6, STATE_SE = 7, STATE_CR = 8, STATE_IAC_SB = 9; private boolean hasReachedEOF; // @GuardedBy("queue") private volatile boolean isClosed; private boolean readIsWaiting; private int receiveState, queueHead, queueTail, bytesAvailable; private final int[] queue; private final TelnetClient client; private final Thread thread; private IOException ioException; /* TERMINAL-TYPE option (start) */ private final int suboption[]; private int suboptionCount; /* TERMINAL-TYPE option (end) */ private volatile boolean threaded; TelnetInputStream(final InputStream input, final TelnetClient client) { this(input, client, true); } TelnetInputStream(final InputStream input, final TelnetClient client, final boolean readerThread) { super(input); this.client = client; this.receiveState = STATE_DATA; this.isClosed = true; this.hasReachedEOF = false; // Make it 2049, because when full, one slot will go unused, and we // want a 2048 byte buffer just to have a round number (base 2 that is) this.queue = new int[2049]; this.queueHead = 0; this.queueTail = 0; this.suboption = new int[client.maxSubnegotiationLength]; this.bytesAvailable = 0; this.ioException = null; this.readIsWaiting = false; this.threaded = false; if (readerThread) { this.thread = new Thread(this); } else { this.thread = null; } } @Override public int available() throws IOException { // Critical section because run() may change bytesAvailable synchronized (queue) { if (threaded) { // Must not call super.available when running threaded: NET-466 return bytesAvailable; } return bytesAvailable + super.available(); } } // Cannot be synchronized. Will cause deadlock if run() is blocked // in read because BufferedInputStream read() is synchronized. @Override public void close() throws IOException { // Completely disregard the fact thread may still be running. // We can't afford to block on this close by waiting for // thread to terminate because few if any JVM's will actually // interrupt a system read() from the interrupt() method. super.close(); synchronized (queue) { hasReachedEOF = true; isClosed = true; if (thread != null && thread.isAlive()) { thread.interrupt(); } queue.notifyAll(); } } /** Returns false. Mark is not supported. */ @Override public boolean markSupported() { return false; } // synchronized(client) critical sections are to protect against // TelnetOutputStream writing through the telnet client at same time // as a processDo/Will/etc. command invoked from TelnetInputStream // tries to write. Returns true if buffer was previously empty. private boolean processChar(final int ch) throws InterruptedException { // Critical section because we're altering bytesAvailable, // queueTail, and the contents of _queue. final boolean bufferWasEmpty; synchronized (queue) { bufferWasEmpty = bytesAvailable == 0; while (bytesAvailable >= queue.length - 1) { // The queue is full. We need to wait before adding any more data to it. Hopefully the stream owner // will consume some data soon! if (!threaded) { // We've been asked to add another character to the queue, but it is already full and there's // no other thread to drain it. This should not have happened! throw new IllegalStateException("Queue is full! Cannot process another character."); } queue.notify(); try { queue.wait(); } catch (final InterruptedException e) { throw e; } } // Need to do this in case we're not full, but block on a read if (readIsWaiting && threaded) { queue.notify(); } queue[queueTail] = ch; ++bytesAvailable; if (++queueTail >= queue.length) { queueTail = 0; } } return bufferWasEmpty; } @Override public int read() throws IOException { // Critical section because we're altering bytesAvailable, // queueHead, and the contents of _queue in addition to // testing value of hasReachedEOF. synchronized (queue) { while (true) { if (ioException != null) { final IOException e; e = ioException; ioException = null; throw e; } if (bytesAvailable == 0) { // Return EOF if at end of file if (hasReachedEOF) { return EOF; } // Otherwise, we have to wait for queue to get something if (threaded) { queue.notify(); try { readIsWaiting = true; queue.wait(); readIsWaiting = false; } catch (final InterruptedException e) { throw new InterruptedIOException("Fatal thread interruption during read."); } } else { // alreadyread = false; readIsWaiting = true; int ch; boolean mayBlock = true; // block on the first read only do { try { if ((ch = read(mayBlock)) < 0) { // must be EOF if (ch != WOULD_BLOCK) { return ch; } } } catch (final InterruptedIOException e) { synchronized (queue) { ioException = e; queue.notifyAll(); try { queue.wait(100); } catch (final InterruptedException interrupted) { // Ignored } } return EOF; } try { if (ch != WOULD_BLOCK) { processChar(ch); } } catch (final InterruptedException e) { if (isClosed) { return EOF; } } // Reads should not block on subsequent iterations. Potentially, this could happen if the // remaining buffered socket data consists entirely of Telnet command sequence and no "user" data. mayBlock = false; } // Continue reading as long as there is data available and the queue is not full. while (super.available() > 0 && bytesAvailable < queue.length - 1); readIsWaiting = false; } continue; } final int ch; ch = queue[queueHead]; if (++queueHead >= queue.length) { queueHead = 0; } --bytesAvailable; // Need to explicitly notify() so available() works properly if (bytesAvailable == 0 && threaded) { queue.notify(); } return ch; } } } // synchronized(client) critical sections are to protect against // TelnetOutputStream writing through the telnet client at same time // as a processDo/Will/etc. command invoked from TelnetInputStream // tries to write. /** * Get the next byte of data. IAC commands are processed internally and do not return data. * * @param mayBlock true if method is allowed to block * @return the next byte of data, or -1 (EOF) if end of stread reached, or -2 (WOULD_BLOCK) if mayBlock is false and there is no data available */ private int read(final boolean mayBlock) throws IOException { int ch; while (true) { // If there is no more data AND we were told not to block, // just return WOULD_BLOCK (-2). (More efficient than exception.) if (!mayBlock && super.available() == 0) { return WOULD_BLOCK; } // Otherwise, exit only when we reach end of stream. if ((ch = super.read()) < 0) { return EOF; } ch = ch & 0xff; /* Code Section added for supporting AYT (start) */ synchronized (client) { client.processAYTResponse(); } /* Code Section added for supporting AYT (end) */ /* Code Section added for supporting spystreams (start) */ client.spyRead(ch); /* Code Section added for supporting spystreams (end) */ switch (receiveState) { case STATE_CR: if (ch == '\0') { // Strip null continue; } // How do we handle newline after cr? // else if (ch == '\n' && _requestedDont(TelnetOption.ECHO) && // Handle as normal data by falling through to _STATE_DATA case //$FALL-THROUGH$ case STATE_DATA: if (ch == TelnetCommand.IAC) { receiveState = STATE_IAC; continue; } if (ch == '\r') { synchronized (client) { if (client.requestedDont(TelnetOption.BINARY)) { receiveState = STATE_CR; } else { receiveState = STATE_DATA; } } } else { receiveState = STATE_DATA; } break; case STATE_IAC: switch (ch) { case TelnetCommand.WILL: receiveState = STATE_WILL; continue; case TelnetCommand.WONT: receiveState = STATE_WONT; continue; case TelnetCommand.DO: receiveState = STATE_DO; continue; case TelnetCommand.DONT: receiveState = STATE_DONT; continue; /* TERMINAL-TYPE option (start) */ case TelnetCommand.SB: suboptionCount = 0; receiveState = STATE_SB; continue; /* TERMINAL-TYPE option (end) */ case TelnetCommand.IAC: receiveState = STATE_DATA; break; // exit to enclosing switch to return IAC from read case TelnetCommand.SE: // unexpected byte! ignore it (don't send it as a command) receiveState = STATE_DATA; continue; default: receiveState = STATE_DATA; client.processCommand(ch); // Notify the user continue; // move on the next char } break; // exit and return from read case STATE_WILL: synchronized (client) { client.processWill(ch); client.flushOutputStream(); } receiveState = STATE_DATA; continue; case STATE_WONT: synchronized (client) { client.processWont(ch); client.flushOutputStream(); } receiveState = STATE_DATA; continue; case STATE_DO: synchronized (client) { client.processDo(ch); client.flushOutputStream(); } receiveState = STATE_DATA; continue; case STATE_DONT: synchronized (client) { client.processDont(ch); client.flushOutputStream(); } receiveState = STATE_DATA; continue; /* TERMINAL-TYPE option (start) */ case STATE_SB: switch (ch) { case TelnetCommand.IAC: receiveState = STATE_IAC_SB; continue; default: // store suboption char if (suboptionCount < suboption.length) { suboption[suboptionCount++] = ch; } break; } receiveState = STATE_SB; continue; case STATE_IAC_SB: // IAC received during SB phase switch (ch) { case TelnetCommand.SE: synchronized (client) { client.processSuboption(suboption, suboptionCount); client.flushOutputStream(); } receiveState = STATE_DATA; continue; case TelnetCommand.IAC: // De-dup the duplicated IAC if (suboptionCount < suboption.length) { suboption[suboptionCount++] = ch; } break; default: // unexpected byte! ignore it break; } receiveState = STATE_SB; continue; /* TERMINAL-TYPE option (end) */ } break; } return ch; } /** * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the stream has been reached. *

* * @param buffer The byte array in which to store the data. * @return The number of bytes read. Returns -1 if the end of the message has been reached. * @throws IOException If an error occurs in reading the underlying stream. */ @Override public int read(final byte buffer[]) throws IOException { return read(buffer, 0, buffer.length); } /** * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the message has been reached. * The characters are stored in the array starting from the given offset and up to the length specified. *

* * @param buffer The byte array in which to store the data. * @param offset The offset into the array at which to start storing data. * @param length The number of bytes to read. * @return The number of bytes read. Returns -1 if the end of the stream has been reached. * @throws IOException If an error occurs while reading the underlying stream. */ @Override public int read(final byte buffer[], int offset, int length) throws IOException { int ch; final int off; if (length < 1) { return 0; } // Critical section because run() may change bytesAvailable synchronized (queue) { if (length > bytesAvailable) { length = bytesAvailable; } } if ((ch = read()) == EOF) { return EOF; } off = offset; do { buffer[offset++] = (byte) ch; } while (--length > 0 && (ch = read()) != EOF); // client._spyRead(buffer, off, offset - off); return offset - off; } @Override public void run() { int ch; try { _outerLoop: while (!isClosed) { try { if ((ch = read(true)) < 0) { break; } } catch (final InterruptedIOException e) { synchronized (queue) { ioException = e; queue.notifyAll(); try { queue.wait(100); } catch (final InterruptedException interrupted) { if (isClosed) { break _outerLoop; } } continue; } } catch (final RuntimeException re) { // We treat any runtime exceptions as though the // stream has been closed. We close the // underlying stream just to be sure. super.close(); // Breaking the loop has the effect of setting // the state to closed at the end of the method. break _outerLoop; } // Process new character boolean notify = false; try { notify = processChar(ch); } catch (final InterruptedException e) { if (isClosed) { break _outerLoop; } } // Notify input listener if buffer was previously empty if (notify) { client.notifyInputListener(); } } } catch (final IOException ioe) { synchronized (queue) { ioException = ioe; } client.notifyInputListener(); } synchronized (queue) { isClosed = true; // Possibly redundant hasReachedEOF = true; queue.notify(); } threaded = false; } void start() { if (thread == null) { return; } int priority; isClosed = false; // TODO remove this // Need to set a higher priority in case JVM does not use pre-emptive // threads. This should prevent scheduler induced deadlock (rather than // deadlock caused by a bug in this code). priority = Thread.currentThread().getPriority() + 1; if (priority > Thread.MAX_PRIORITY) { priority = Thread.MAX_PRIORITY; } thread.setPriority(priority); thread.setDaemon(true); thread.start(); threaded = true; // tell _processChar that we are running threaded } } TelnetNotificationHandler.java000066400000000000000000000041471434047722200350760ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * The TelnetNotificationHandler interface can be used to handle notification of options negotiation commands received on a telnet session. *

* The user can implement this interface and register a TelnetNotificationHandler by using the registerNotificationHandler() of TelnetClient to be notified of * option negotiation commands. */ public interface TelnetNotificationHandler { /** * The remote party sent a DO command. */ int RECEIVED_DO = 1; /** * The remote party sent a DONT command. */ int RECEIVED_DONT = 2; /** * The remote party sent a WILL command. */ int RECEIVED_WILL = 3; /** * The remote party sent a WONT command. */ int RECEIVED_WONT = 4; /** * The remote party sent a COMMAND. * * @since 2.2 */ int RECEIVED_COMMAND = 5; /** * Callback method called when TelnetClient receives an command or option negotiation command * * @param negotiation_code - type of (negotiation) command received (RECEIVED_DO, RECEIVED_DONT, RECEIVED_WILL, RECEIVED_WONT, RECEIVED_COMMAND) * * @param option_code - code of the option negotiated, or the command code itself (e.g. NOP). */ void receivedNegotiation(int negotiation_code, int option_code); } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/TelnetOption.java000066400000000000000000000140221434047722200324720ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * The TelnetOption class cannot be instantiated and only serves as a storehouse for telnet option constants. *

* Details regarding Telnet option specification can be found in RFC 855. * * * @see org.apache.commons.net.telnet.Telnet * @see org.apache.commons.net.telnet.TelnetClient */ public class TelnetOption { /** The maximum value an option code can have. This value is 255. */ public static final int MAX_OPTION_VALUE = 255; public static final int BINARY = 0; public static final int ECHO = 1; public static final int PREPARE_TO_RECONNECT = 2; public static final int SUPPRESS_GO_AHEAD = 3; public static final int APPROXIMATE_MESSAGE_SIZE = 4; public static final int STATUS = 5; public static final int TIMING_MARK = 6; public static final int REMOTE_CONTROLLED_TRANSMISSION = 7; public static final int NEGOTIATE_OUTPUT_LINE_WIDTH = 8; public static final int NEGOTIATE_OUTPUT_PAGE_SIZE = 9; public static final int NEGOTIATE_CARRIAGE_RETURN = 10; public static final int NEGOTIATE_HORIZONTAL_TAB_STOP = 11; public static final int NEGOTIATE_HORIZONTAL_TAB = 12; public static final int NEGOTIATE_FORMFEED = 13; public static final int NEGOTIATE_VERTICAL_TAB_STOP = 14; public static final int NEGOTIATE_VERTICAL_TAB = 15; public static final int NEGOTIATE_LINEFEED = 16; public static final int EXTENDED_ASCII = 17; public static final int FORCE_LOGOUT = 18; public static final int BYTE_MACRO = 19; public static final int DATA_ENTRY_TERMINAL = 20; public static final int SUPDUP = 21; public static final int SUPDUP_OUTPUT = 22; public static final int SEND_LOCATION = 23; public static final int TERMINAL_TYPE = 24; public static final int END_OF_RECORD = 25; public static final int TACACS_USER_IDENTIFICATION = 26; public static final int OUTPUT_MARKING = 27; public static final int TERMINAL_LOCATION_NUMBER = 28; public static final int REGIME_3270 = 29; public static final int X3_PAD = 30; public static final int WINDOW_SIZE = 31; public static final int TERMINAL_SPEED = 32; public static final int REMOTE_FLOW_CONTROL = 33; public static final int LINEMODE = 34; public static final int X_DISPLAY_LOCATION = 35; public static final int OLD_ENVIRONMENT_VARIABLES = 36; public static final int AUTHENTICATION = 37; public static final int ENCRYPTION = 38; public static final int NEW_ENVIRONMENT_VARIABLES = 39; public static final int EXTENDED_OPTIONS_LIST = 255; @SuppressWarnings("unused") private static final int FIRST_OPTION = BINARY; private static final int LAST_OPTION = EXTENDED_OPTIONS_LIST; private static final String optionString[] = { "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP", "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO", "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD", "TACACS UID", "OUTPUT MARKING", "TTYLOC", "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON", "TN3270E", "XAUTH", "CHARSET", "RSP", "Com Port Control", "Suppress Local Echo", "Start TLS", "KERMIT", "SEND-URL", "FORWARD_X", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "TELOPT PRAGMA LOGON", "TELOPT SSPI LOGON", "TELOPT PRAGMA HEARTBEAT", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Extended-Options-List" }; /** * Returns the string representation of the telnet protocol option corresponding to the given option code. * * @param code The option code of the telnet protocol option * @return The string representation of the telnet protocol option. */ public static final String getOption(final int code) { if (optionString[code].isEmpty()) { return "UNASSIGNED"; } return optionString[code]; } /** * Determines if a given option code is valid. Returns true if valid, false if not. * * @param code The option code to test. * @return True if the option code is valid, false if not. **/ public static final boolean isValidOption(final int code) { return code <= LAST_OPTION; } // Cannot be instantiated private TelnetOption() { } } TelnetOptionHandler.java000066400000000000000000000204201434047722200337100ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * The TelnetOptionHandler class is the base class to be used for implementing handlers for telnet options. *

* TelnetOptionHandler implements basic option handling functionality and defines abstract methods that must be implemented to define subnegotiation behavior. */ public abstract class TelnetOptionHandler { /** * Option code */ private int optionCode = -1; /** * true if the option should be activated on the local side */ private boolean initialLocal; /** * true if the option should be activated on the remote side */ private boolean initialRemote; /** * true if the option should be accepted on the local side */ private boolean acceptLocal; /** * true if the option should be accepted on the remote side */ private boolean acceptRemote; /** * true if the option is active on the local side */ private boolean doFlag; /** * true if the option is active on the remote side */ private boolean willFlag; /** * Constructor for the TelnetOptionHandler. Allows defining desired initial setting for local/remote activation of this option and behavior in case a * local/remote activation request for this option is received. *

* * @param optcode - Option code. * @param initlocal - if set to true, a WILL is sent upon connection. * @param initremote - if set to true, a DO is sent upon connection. * @param acceptlocal - if set to true, any DO request is accepted. * @param acceptremote - if set to true, any WILL request is accepted. */ public TelnetOptionHandler(final int optcode, final boolean initlocal, final boolean initremote, final boolean acceptlocal, final boolean acceptremote) { optionCode = optcode; initialLocal = initlocal; initialRemote = initremote; acceptLocal = acceptlocal; acceptRemote = acceptremote; } /** * Method called upon reception of a subnegotiation for this option coming from the other end. *

* This implementation returns null, and must be overridden by the actual TelnetOptionHandler to specify which response must be sent for the subnegotiation * request. *

* * @param suboptionData - the sequence received, without IAC SB & IAC SE * @param suboptionLength - the length of data in suboption_data *

* @return response to be sent to the subnegotiation sequence. TelnetClient will add IAC SB & IAC SE. null means no response */ public int[] answerSubnegotiation(final int suboptionData[], final int suboptionLength) { return null; } /** * Returns a boolean indicating whether to accept a DO request coming from the other end. *

* * @return true if a DO request shall be accepted. */ public boolean getAcceptLocal() { return acceptLocal; } /** * Returns a boolean indicating whether to accept a WILL request coming from the other end. *

* * @return true if a WILL request shall be accepted. */ public boolean getAcceptRemote() { return acceptRemote; } /** * Returns a boolean indicating whether a DO request sent to the other side has been acknowledged. *

* * @return true if a DO sent to the other side has been acknowledged. */ boolean getDo() { return doFlag; } /** * Returns a boolean indicating whether to send a WILL request to the other end upon connection. *

* * @return true if a WILL request shall be sent upon connection. */ public boolean getInitLocal() { return initialLocal; } /** * Returns a boolean indicating whether to send a DO request to the other end upon connection. *

* * @return true if a DO request shall be sent upon connection. */ public boolean getInitRemote() { return initialRemote; } /** * Returns the option code for this option. *

* * @return Option code. */ public int getOptionCode() { return optionCode; } /** * Returns a boolean indicating whether a WILL request sent to the other side has been acknowledged. *

* * @return true if a WILL sent to the other side has been acknowledged. */ boolean getWill() { return willFlag; } /** * Set behavior of the option for DO requests coming from the other end. *

* * @param accept - if true, subsequent DO requests will be accepted. */ public void setAcceptLocal(final boolean accept) { acceptLocal = accept; } /** * Set behavior of the option for WILL requests coming from the other end. *

* * @param accept - if true, subsequent WILL requests will be accepted. */ public void setAcceptRemote(final boolean accept) { acceptRemote = accept; } /** * Tells this option whether a DO request sent to the other side has been acknowledged (invoked by TelnetClient). *

* * @param state - if true, a DO request has been acknowledged. */ void setDo(final boolean state) { doFlag = state; } /** * Tells this option whether to send a WILL request upon connection. *

* * @param init - if true, a WILL request will be sent upon subsequent connections. */ public void setInitLocal(final boolean init) { initialLocal = init; } /** * Tells this option whether to send a DO request upon connection. *

* * @param init - if true, a DO request will be sent upon subsequent connections. */ public void setInitRemote(final boolean init) { initialRemote = init; } /** * Tells this option whether a WILL request sent to the other side has been acknowledged (invoked by TelnetClient). *

* * @param state - if true, a WILL request has been acknowledged. */ void setWill(final boolean state) { willFlag = state; } /** * This method is invoked whenever this option is acknowledged active on the local end (TelnetClient sent a WILL, remote side sent a DO). The method is used * to specify a subnegotiation sequence that will be sent by TelnetClient when the option is activated. *

* This implementation returns null, and must be overriden by the actual TelnetOptionHandler to specify which response must be sent for the subnegotiation * request. * * @return subnegotiation sequence to be sent by TelnetClient. TelnetClient will add IAC SB & IAC SE. null means no subnegotiation. */ public int[] startSubnegotiationLocal() { return null; } /** * This method is invoked whenever this option is acknowledged active on the remote end (TelnetClient sent a DO, remote side sent a WILL). The method is * used to specify a subnegotiation sequence that will be sent by TelnetClient when the option is activated. *

* This implementation returns null, and must be overriden by the actual TelnetOptionHandler to specify which response must be sent for the subnegotiation * request. * * @return subnegotiation sequence to be sent by TelnetClient. TelnetClient will add IAC SB & IAC SE. null means no subnegotiation. */ public int[] startSubnegotiationRemote() { return null; } } TelnetOutputStream.java000066400000000000000000000112651434047722200336250ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.IOException; import java.io.OutputStream; /** * Wraps an output stream. *

* In binary mode, the only conversion is to double IAC. *

* In ASCII mode, if convertCRtoCRLF is true (currently always true), any CR is converted to CRLF. IACs are doubled. Also a bare LF is converted to CRLF and a * bare CR is converted to CR\0 *

*/ final class TelnetOutputStream extends OutputStream { private final TelnetClient client; // TODO there does not appear to be any way to change this value - should it be a ctor parameter? private final boolean convertCRtoCRLF = true; private boolean lastWasCR; TelnetOutputStream(final TelnetClient client) { this.client = client; } /** Closes the stream. */ @Override public void close() throws IOException { client.closeOutputStream(); } /** Flushes the stream. */ @Override public void flush() throws IOException { client.flushOutputStream(); } /** * Writes a byte array to the stream. *

* * @param buffer The byte array to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public void write(final byte buffer[]) throws IOException { write(buffer, 0, buffer.length); } /** * Writes a number of bytes from a byte array to the stream starting from a given offset. *

* * @param buffer The byte array to write. * @param offset The offset into the array at which to start copying data. * @param length The number of bytes to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public void write(final byte buffer[], int offset, int length) throws IOException { synchronized (client) { while (length-- > 0) { write(buffer[offset++]); } } } /** * Writes a byte to the stream. *

* * @param ch The byte to write. * @throws IOException If an error occurs while writing to the underlying stream. */ @Override public void write(int ch) throws IOException { synchronized (client) { ch &= 0xff; if (client.requestedWont(TelnetOption.BINARY)) // i.e. ASCII { if (lastWasCR) { if (convertCRtoCRLF) { client.sendByte('\n'); if (ch == '\n') // i.e. was CRLF anyway { lastWasCR = false; return; } } // __convertCRtoCRLF else if (ch != '\n') { client.sendByte('\0'); // RFC854 requires CR NUL for bare CR } } switch (ch) { case '\r': client.sendByte('\r'); lastWasCR = true; break; case '\n': if (!lastWasCR) { // convert LF to CRLF client.sendByte('\r'); } client.sendByte(ch); lastWasCR = false; break; case TelnetCommand.IAC: client.sendByte(TelnetCommand.IAC); client.sendByte(TelnetCommand.IAC); lastWasCR = false; break; default: client.sendByte(ch); lastWasCR = false; break; } } // end ASCII else if (ch == TelnetCommand.IAC) { client.sendByte(ch); client.sendByte(TelnetCommand.IAC); } else { client.sendByte(ch); } } } } TerminalTypeOptionHandler.java000066400000000000000000000071331434047722200351000ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * Implements the telnet terminal type option RFC 1091. */ public class TerminalTypeOptionHandler extends TelnetOptionHandler { /** * Terminal type option */ protected static final int TERMINAL_TYPE = 24; /** * Send (for subnegotiation) */ protected static final int TERMINAL_TYPE_SEND = 1; /** * Is (for subnegotiation) */ protected static final int TERMINAL_TYPE_IS = 0; /** * Terminal type */ private final String termType; /** * Constructor for the TerminalTypeOptionHandler. Initial and accept behavior flags are set to false *

* * @param termtype - terminal type that will be negotiated. */ public TerminalTypeOptionHandler(final String termtype) { super(TelnetOption.TERMINAL_TYPE, false, false, false, false); termType = termtype; } /** * Constructor for the TerminalTypeOptionHandler. Allows defining desired initial setting for local/remote activation of this option and behavior in case a * local/remote activation request for this option is received. *

* * @param termtype - terminal type that will be negotiated. * @param initlocal - if set to true, a WILL is sent upon connection. * @param initremote - if set to true, a DO is sent upon connection. * @param acceptlocal - if set to true, any DO request is accepted. * @param acceptremote - if set to true, any WILL request is accepted. */ public TerminalTypeOptionHandler(final String termtype, final boolean initlocal, final boolean initremote, final boolean acceptlocal, final boolean acceptremote) { super(TelnetOption.TERMINAL_TYPE, initlocal, initremote, acceptlocal, acceptremote); termType = termtype; } /** * Implements the abstract method of TelnetOptionHandler. *

* * @param suboptionData - the sequence received, without IAC SB & IAC SE * @param suboptionLength - the length of data in suboption_data *

* @return terminal type information */ @Override public int[] answerSubnegotiation(final int suboptionData[], final int suboptionLength) { if ((suboptionData != null) && (suboptionLength > 1) && (termType != null)) { if ((suboptionData[0] == TERMINAL_TYPE) && (suboptionData[1] == TERMINAL_TYPE_SEND)) { final int response[] = new int[termType.length() + 2]; response[0] = TERMINAL_TYPE; response[1] = TERMINAL_TYPE_IS; for (int ii = 0; ii < termType.length(); ii++) { response[ii + 2] = termType.charAt(ii); } return response; } } return null; } } WindowSizeOptionHandler.java000066400000000000000000000101321434047722200345560ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * Implements the telnet window size option RFC 1073. * * @since 2.0 */ public class WindowSizeOptionHandler extends TelnetOptionHandler { /** * Window size option */ protected static final int WINDOW_SIZE = 31; /** * Horizontal Size */ private int width = 80; /** * Vertical Size */ private int height = 24; /** * Constructor for the WindowSizeOptionHandler. Initial and accept behavior flags are set to false *

* * @param nWidth - Window width. * @param nHeight - Window Height */ public WindowSizeOptionHandler(final int nWidth, final int nHeight) { super(TelnetOption.WINDOW_SIZE, false, false, false, false); width = nWidth; height = nHeight; } /** * Constructor for the WindowSizeOptionHandler. Allows defining desired initial setting for local/remote activation of this option and behavior in case a * local/remote activation request for this option is received. *

* * @param nWidth - Window width. * @param nHeight - Window Height * @param initlocal - if set to true, a WILL is sent upon connection. * @param initremote - if set to true, a DO is sent upon connection. * @param acceptlocal - if set to true, any DO request is accepted. * @param acceptremote - if set to true, any WILL request is accepted. */ public WindowSizeOptionHandler(final int nWidth, final int nHeight, final boolean initlocal, final boolean initremote, final boolean acceptlocal, final boolean acceptremote) { super(TelnetOption.WINDOW_SIZE, initlocal, initremote, acceptlocal, acceptremote); width = nWidth; height = nHeight; } /** * Implements the abstract method of TelnetOptionHandler. This will send the client Height and Width to the server. *

* * @return array to send to remote system */ @Override public int[] startSubnegotiationLocal() { final int nCompoundWindowSize = width * 0x10000 + height; int nResponseSize = 5; int nIndex; int nShift; int nTurnedOnBits; if (width % 0x100 == 0xFF) { nResponseSize += 1; } if (width / 0x100 == 0xFF) { nResponseSize += 1; } if (height % 0x100 == 0xFF) { nResponseSize += 1; } if (height / 0x100 == 0xFF) { nResponseSize += 1; } // // allocate response array // final int response[] = new int[nResponseSize]; // // Build response array. // --------------------- // 1. put option name. // 2. loop through Window size and fill the values, // 3. duplicate 'ff' if needed. // response[0] = WINDOW_SIZE; // 1 // for ( // 2 // nIndex = 1, nShift = 24; nIndex < nResponseSize; nIndex++, nShift -= 8) { nTurnedOnBits = 0xFF; nTurnedOnBits <<= nShift; response[nIndex] = (nCompoundWindowSize & nTurnedOnBits) >>> nShift; if (response[nIndex] == 0xff) { // 3 // nIndex++; response[nIndex] = 0xff; } } return response; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/telnet/package-info.java000066400000000000000000000015531434047722200323770ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Telnet implementation */ package org.apache.commons.net.telnet;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/000077500000000000000000000000001434047722200266665ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTP.java000066400000000000000000000261551434047722200303170ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.io.IOException; import java.io.InterruptedIOException; import java.net.DatagramPacket; import java.net.SocketException; import org.apache.commons.net.DatagramSocketClient; /** * The TFTP class exposes a set of methods to allow you to deal with the TFTP protocol directly, in case you want to write your own TFTP client or server. * However, almost every user should only be concerend with the {@link org.apache.commons.net.DatagramSocketClient#open open() }, and * {@link org.apache.commons.net.DatagramSocketClient#close close() }, methods. Additionally,the a * {@link org.apache.commons.net.DatagramSocketClient#setDefaultTimeout setDefaultTimeout() } method may be of importance for performance tuning. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. * * * @see org.apache.commons.net.DatagramSocketClient * @see TFTPPacket * @see TFTPPacketException * @see TFTPClient */ public class TFTP extends DatagramSocketClient { /** * The ascii transfer mode. Its value is 0 and equivalent to NETASCII_MODE */ public static final int ASCII_MODE = 0; /** * The netascii transfer mode. Its value is 0. */ public static final int NETASCII_MODE = 0; /** * The binary transfer mode. Its value is 1 and equivalent to OCTET_MODE. */ public static final int BINARY_MODE = 1; /** * The image transfer mode. Its value is 1 and equivalent to OCTET_MODE. */ public static final int IMAGE_MODE = 1; /** * The octet transfer mode. Its value is 1. */ public static final int OCTET_MODE = 1; /** * The default number of milliseconds to wait to receive a datagram before timing out. The default is 5000 milliseconds (5 seconds). */ public static final int DEFAULT_TIMEOUT = 5000; /** * The default TFTP port according to RFC 783 is 69. */ public static final int DEFAULT_PORT = 69; /** * The size to use for TFTP packet buffers. Its 4 plus the TFTPPacket.SEGMENT_SIZE, i.e. 516. */ static final int PACKET_SIZE = TFTPPacket.SEGMENT_SIZE + 4; /** * Returns the TFTP string representation of a TFTP transfer mode. Will throw an ArrayIndexOutOfBoundsException if an invalid transfer mode is specified. * * @param mode The TFTP transfer mode. One of the MODE constants. * @return The TFTP string representation of the TFTP transfer mode. */ public static final String getModeName(final int mode) { return TFTPRequestPacket.modeStrings[mode]; } /** A buffer used to accelerate receives in bufferedReceive() */ private byte[] receiveBuffer; /** A datagram used to minimize memory allocation in bufferedReceive() */ private DatagramPacket receiveDatagram; /** A datagram used to minimize memory allocation in bufferedSend() */ private DatagramPacket sendDatagram; /** * A buffer used to accelerate sends in bufferedSend(). It is left package visible so that TFTPClient may be slightly more efficient during file sends. It * saves the creation of an additional buffer and prevents a buffer copy in _newDataPcket(). */ byte[] sendBuffer; /** * Creates a TFTP instance with a default timeout of DEFAULT_TIMEOUT, a null socket, and buffered operations disabled. */ public TFTP() { setDefaultTimeout(DEFAULT_TIMEOUT); receiveBuffer = null; receiveDatagram = null; } /** * Initializes the internal buffers. Buffers are used by {@link #bufferedSend bufferedSend() } and {@link #bufferedReceive bufferedReceive() }. This method * must be called before calling either one of those two methods. When you finish using buffered operations, you must call {@link #endBufferedOps * endBufferedOps() }. */ public final void beginBufferedOps() { receiveBuffer = new byte[PACKET_SIZE]; receiveDatagram = new DatagramPacket(receiveBuffer, receiveBuffer.length); sendBuffer = new byte[PACKET_SIZE]; sendDatagram = new DatagramPacket(sendBuffer, sendBuffer.length); } /** * This is a special method to perform a more efficient packet receive. It should only be used after calling {@link #beginBufferedOps beginBufferedOps() }. * beginBufferedOps() initializes a set of buffers used internally that prevent the new allocation of a DatagramPacket and byte array for each send and * receive. To use these buffers you must call the bufferedReceive() and bufferedSend() methods instead of send() and receive(). You must also be certain * that you don't manipulate the resulting packet in such a way that it interferes with future buffered operations. For example, a TFTPDataPacket received * with bufferedReceive() will have a reference to the internal byte buffer. You must finish using this data before calling bufferedReceive() again, or else * the data will be overwritten by the the call. * * @return The TFTPPacket received. * @throws InterruptedIOException If a socket timeout occurs. The Java documentation claims an InterruptedIOException is thrown on a DatagramSocket timeout, * but in practice we find a SocketException is thrown. You should catch both to be safe. * @throws SocketException If a socket timeout occurs. The Java documentation claims an InterruptedIOException is thrown on a DatagramSocket timeout, * but in practice we find a SocketException is thrown. You should catch both to be safe. * @throws IOException If some other I/O error occurs. * @throws TFTPPacketException If an invalid TFTP packet is received. */ public final TFTPPacket bufferedReceive() throws IOException, InterruptedIOException, SocketException, TFTPPacketException { receiveDatagram.setData(receiveBuffer); receiveDatagram.setLength(receiveBuffer.length); _socket_.receive(receiveDatagram); final TFTPPacket newTFTPPacket = TFTPPacket.newTFTPPacket(receiveDatagram); trace("<", newTFTPPacket); return newTFTPPacket; } /** * This is a special method to perform a more efficient packet send. It should only be used after calling {@link #beginBufferedOps beginBufferedOps() }. * beginBufferedOps() initializes a set of buffers used internally that prevent the new allocation of a DatagramPacket and byte array for each send and * receive. To use these buffers you must call the bufferedReceive() and bufferedSend() methods instead of send() and receive(). You must also be certain * that you don't manipulate the resulting packet in such a way that it interferes with future buffered operations. For example, a TFTPDataPacket received * with bufferedReceive() will have a reference to the internal byte buffer. You must finish using this data before calling bufferedReceive() again, or else * the data will be overwritten by the the call. * * @param packet The TFTP packet to send. * @throws IOException If some I/O error occurs. */ public final void bufferedSend(final TFTPPacket packet) throws IOException { trace(">", packet); _socket_.send(packet.newDatagram(sendDatagram, sendBuffer)); } /** * This method synchronizes a connection by discarding all packets that may be in the local socket buffer. This method need only be called when you * implement your own TFTP client or server. * * @throws IOException if an I/O error occurs. */ public final void discardPackets() throws IOException { final int to; final DatagramPacket datagram; datagram = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE); to = getSoTimeout(); setSoTimeout(1); try { while (true) { _socket_.receive(datagram); } } catch (final SocketException | InterruptedIOException e) { // Do nothing. We timed out so we hope we're caught up. } setSoTimeout(to); } /** * Releases the resources used to perform buffered sends and receives. */ public final void endBufferedOps() { receiveBuffer = null; receiveDatagram = null; sendBuffer = null; sendDatagram = null; } /** * Receives a TFTPPacket. * * @return The TFTPPacket received. * @throws InterruptedIOException If a socket timeout occurs. The Java documentation claims an InterruptedIOException is thrown on a DatagramSocket timeout, * but in practice we find a SocketException is thrown. You should catch both to be safe. * @throws SocketException If a socket timeout occurs. The Java documentation claims an InterruptedIOException is thrown on a DatagramSocket timeout, * but in practice we find a SocketException is thrown. You should catch both to be safe. * @throws IOException If some other I/O error occurs. * @throws TFTPPacketException If an invalid TFTP packet is received. */ public final TFTPPacket receive() throws IOException, InterruptedIOException, SocketException, TFTPPacketException { final DatagramPacket packet; packet = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE); _socket_.receive(packet); final TFTPPacket newTFTPPacket = TFTPPacket.newTFTPPacket(packet); trace("<", newTFTPPacket); return newTFTPPacket; } /** * Sends a TFTP packet to its destination. * * @param packet The TFTP packet to send. * @throws IOException If some I/O error occurs. */ public final void send(final TFTPPacket packet) throws IOException { trace(">", packet); _socket_.send(packet.newDatagram()); } /** * Trace facility; this implementation does nothing. *

* Override it to trace the data, for example:
* {@code System.out.println(direction + " " + packet.toString());} * * @param direction {@code >} or {@code <} * @param packet the packet to be sent or that has been received respectively * @since 3.6 */ protected void trace(final String direction, final TFTPPacket packet) { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTPAckPacket.java000066400000000000000000000125331434047722200320610ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.net.DatagramPacket; import java.net.InetAddress; /** * A final class derived from TFTPPacket definiing the TFTP Acknowledgement packet type. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods. * * * @see TFTPPacket * @see TFTPPacketException * @see TFTP */ public final class TFTPAckPacket extends TFTPPacket { /** The block number being acknowledged by the packet. */ int blockNumber; /** * Creates an acknowledgement packet based from a received datagram. Assumes the datagram is at least length 4, else an ArrayIndexOutOfBoundsException may * be thrown. * * @param datagram The datagram containing the received acknowledgement. * @throws TFTPPacketException If the datagram isn't a valid TFTP acknowledgement packet. */ TFTPAckPacket(final DatagramPacket datagram) throws TFTPPacketException { super(TFTPPacket.ACKNOWLEDGEMENT, datagram.getAddress(), datagram.getPort()); final byte[] data; data = datagram.getData(); if (getType() != data[1]) { throw new TFTPPacketException("TFTP operator code does not match type."); } this.blockNumber = (((data[2] & 0xff) << 8) | (data[3] & 0xff)); } /** * Creates an acknowledgment packet to be sent to a host at a given port acknowledging receipt of a block. * * @param destination The host to which the packet is going to be sent. * @param port The port to which the packet is going to be sent. * @param blockNumber The block number being acknowledged. */ public TFTPAckPacket(final InetAddress destination, final int port, final int blockNumber) { super(TFTPPacket.ACKNOWLEDGEMENT, destination, port); this.blockNumber = blockNumber; } /** * Returns the block number of the acknowledgement. * * @return The block number of the acknowledgement. */ public int getBlockNumber() { return blockNumber; } /** * Creates a UDP datagram containing all the TFTP acknowledgement packet data in the proper format. This is a method exposed to the programmer in case he * wants to implement his own TFTP client instead of using the {@link org.apache.commons.net.tftp.TFTPClient} class. Under normal circumstances, you should * not have a need to call this method. * * @return A UDP datagram containing the TFTP acknowledgement packet. */ @Override public DatagramPacket newDatagram() { final byte[] data; data = new byte[4]; data[0] = 0; data[1] = (byte) type; data[2] = (byte) ((blockNumber & 0xffff) >> 8); data[3] = (byte) (blockNumber & 0xff); return new DatagramPacket(data, data.length, address, port); } /** * This is a method only available within the package for implementing efficient datagram transport by elminating buffering. It takes a datagram as an * argument, and a byte buffer in which to store the raw datagram data. Inside the method, the data is set as the datagram's data and the datagram returned. * * @param datagram The datagram to create. * @param data The buffer to store the packet and to use in the datagram. * @return The datagram argument. */ @Override DatagramPacket newDatagram(final DatagramPacket datagram, final byte[] data) { data[0] = 0; data[1] = (byte) type; data[2] = (byte) ((blockNumber & 0xffff) >> 8); data[3] = (byte) (blockNumber & 0xff); datagram.setAddress(address); datagram.setPort(port); datagram.setData(data); datagram.setLength(4); return datagram; } /** * Sets the block number of the acknowledgement. * * @param blockNumber the number to set */ public void setBlockNumber(final int blockNumber) { this.blockNumber = blockNumber; } /** * For debugging * * @since 3.6 */ @Override public String toString() { return super.toString() + " ACK " + blockNumber; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTPClient.java000066400000000000000000000553771434047722200314660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import org.apache.commons.net.io.FromNetASCIIOutputStream; import org.apache.commons.net.io.ToNetASCIIInputStream; /** * The TFTPClient class encapsulates all the aspects of the TFTP protocol necessary to receive and send files through TFTP. It is derived from the * {@link org.apache.commons.net.tftp.TFTP} because it is more convenient than using aggregation, and as a result exposes the same set of methods to allow you * to deal with the TFTP protocol directly. However, almost every user should only be concerend with the the * {@link org.apache.commons.net.DatagramSocketClient#open open() }, {@link org.apache.commons.net.DatagramSocketClient#close close() }, {@link #sendFile * sendFile() }, and {@link #receiveFile receiveFile() } methods. Additionally, the {@link #setMaxTimeouts setMaxTimeouts() } and * {@link org.apache.commons.net.DatagramSocketClient#setDefaultTimeout setDefaultTimeout() } methods may be of importance for performance tuning. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. * * * @see TFTP * @see TFTPPacket * @see TFTPPacketException */ public class TFTPClient extends TFTP { /** * The default number of times a receive attempt is allowed to timeout before ending attempts to retry the receive and failing. The default is 5 timeouts. */ public static final int DEFAULT_MAX_TIMEOUTS = 5; /** The maximum number of timeouts allowed before failing. */ private int maxTimeouts; /** The number of bytes received in the ongoing download. */ private long totalBytesReceived; /** The number of bytes sent in the ongoing upload. */ private long totalBytesSent; /** * Creates a TFTPClient instance with a default timeout of DEFAULT_TIMEOUT, maximum timeouts value of DEFAULT_MAX_TIMEOUTS, a null socket, and buffered * operations disabled. */ public TFTPClient() { maxTimeouts = DEFAULT_MAX_TIMEOUTS; } /** * Returns the maximum number of times a receive attempt is allowed to timeout before ending attempts to retry the receive and failing. * * @return The maximum number of timeouts allowed. */ public int getMaxTimeouts() { return maxTimeouts; } /** * @return The number of bytes received in the ongoing download */ public long getTotalBytesReceived() { return totalBytesReceived; } /** * @return The number of bytes sent in the ongoing download */ public long getTotalBytesSent() { return totalBytesSent; } /** * Same as calling receiveFile(fileName, mode, output, host, TFTP.DEFAULT_PORT). * * @param fileName The name of the file to receive. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param output The OutputStream to which the file should be written. * @param host The remote host serving the file. * @return number of bytes read * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. */ public int receiveFile(final String fileName, final int mode, final OutputStream output, final InetAddress host) throws IOException { return receiveFile(fileName, mode, output, host, DEFAULT_PORT); } /** * Requests a named file from a remote host, writes the file to an OutputStream, closes the connection, and returns the number of bytes read. A local UDP * socket must first be created by {@link org.apache.commons.net.DatagramSocketClient#open open()} before invoking this method. This method will not close * the OutputStream containing the file; you must close it after the method invocation. * * @param fileName The name of the file to receive. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param output The OutputStream to which the file should be written. * @param host The remote host serving the file. * @param port The port number of the remote TFTP server. * @return number of bytes read * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. */ public int receiveFile(final String fileName, final int mode, OutputStream output, InetAddress host, final int port) throws IOException { int bytesRead = 0; int lastBlock = 0; int block = 1; int hostPort = 0; int dataLength = 0; totalBytesReceived = 0; if (mode == TFTP.ASCII_MODE) { output = new FromNetASCIIOutputStream(output); } TFTPPacket sent = new TFTPReadRequestPacket(host, port, fileName, mode); final TFTPAckPacket ack = new TFTPAckPacket(host, port, 0); beginBufferedOps(); boolean justStarted = true; try { do { // while more data to fetch bufferedSend(sent); // start the fetch/send an ack boolean wantReply = true; int timeouts = 0; do { // until successful response try { final TFTPPacket received = bufferedReceive(); // The first time we receive we get the port number and // answering host address (for hosts with multiple IPs) final int recdPort = received.getPort(); final InetAddress recdAddress = received.getAddress(); if (justStarted) { justStarted = false; if (recdPort == port) { // must not use the control port here final TFTPErrorPacket error = new TFTPErrorPacket(recdAddress, recdPort, TFTPErrorPacket.UNKNOWN_TID, "INCORRECT SOURCE PORT"); bufferedSend(error); throw new IOException("Incorrect source port (" + recdPort + ") in request reply."); } hostPort = recdPort; ack.setPort(hostPort); if (!host.equals(recdAddress)) { host = recdAddress; ack.setAddress(host); sent.setAddress(host); } } // Comply with RFC 783 indication that an error acknowledgment // should be sent to originator if unexpected TID or host. if (host.equals(recdAddress) && recdPort == hostPort) { switch (received.getType()) { case TFTPPacket.ERROR: TFTPErrorPacket error = (TFTPErrorPacket) received; throw new IOException("Error code " + error.getError() + " received: " + error.getMessage()); case TFTPPacket.DATA: final TFTPDataPacket data = (TFTPDataPacket) received; dataLength = data.getDataLength(); lastBlock = data.getBlockNumber(); if (lastBlock == block) { // is the next block number? try { output.write(data.getData(), data.getDataOffset(), dataLength); } catch (final IOException e) { error = new TFTPErrorPacket(host, hostPort, TFTPErrorPacket.OUT_OF_SPACE, "File write failed."); bufferedSend(error); throw e; } ++block; if (block > 65535) { // wrap the block number block = 0; } wantReply = false; // got the next block, drop out to ack it } else { // unexpected block number discardPackets(); if (lastBlock == (block == 0 ? 65535 : block - 1)) { wantReply = false; // Resend last acknowledgemen } } break; default: throw new IOException("Received unexpected packet type (" + received.getType() + ")"); } } else { // incorrect host or TID final TFTPErrorPacket error = new TFTPErrorPacket(recdAddress, recdPort, TFTPErrorPacket.UNKNOWN_TID, "Unexpected host or port."); bufferedSend(error); } } catch (final SocketException | InterruptedIOException e) { if (++timeouts >= maxTimeouts) { throw new IOException("Connection timed out."); } } catch (final TFTPPacketException e) { throw new IOException("Bad packet: " + e.getMessage()); } } while (wantReply); // waiting for response ack.setBlockNumber(lastBlock); sent = ack; bytesRead += dataLength; totalBytesReceived += dataLength; } while (dataLength == TFTPPacket.SEGMENT_SIZE); // not eof bufferedSend(sent); // send the final ack } finally { endBufferedOps(); } return bytesRead; } /** * Same as calling receiveFile(fileName, mode, output, hostname, TFTP.DEFAULT_PORT). * * @param fileName The name of the file to receive. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param output The OutputStream to which the file should be written. * @param hostname The name of the remote host serving the file. * @return number of bytes read * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. * @throws UnknownHostException If the hostname cannot be resolved. */ public int receiveFile(final String fileName, final int mode, final OutputStream output, final String hostname) throws UnknownHostException, IOException { return receiveFile(fileName, mode, output, InetAddress.getByName(hostname), DEFAULT_PORT); } /** * Requests a named file from a remote host, writes the file to an OutputStream, closes the connection, and returns the number of bytes read. A local UDP * socket must first be created by {@link org.apache.commons.net.DatagramSocketClient#open open()} before invoking this method. This method will not close * the OutputStream containing the file; you must close it after the method invocation. * * @param fileName The name of the file to receive. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param output The OutputStream to which the file should be written. * @param hostname The name of the remote host serving the file. * @param port The port number of the remote TFTP server. * @return number of bytes read * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. * @throws UnknownHostException If the hostname cannot be resolved. */ public int receiveFile(final String fileName, final int mode, final OutputStream output, final String hostname, final int port) throws UnknownHostException, IOException { return receiveFile(fileName, mode, output, InetAddress.getByName(hostname), port); } /** * Same as calling sendFile(fileName, mode, input, host, TFTP.DEFAULT_PORT). * * @param fileName The name the remote server should use when creating the file on its file system. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param input the input stream containing the data to be sent * @param host The name of the remote host receiving the file. * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. * @throws UnknownHostException If the hostname cannot be resolved. */ public void sendFile(final String fileName, final int mode, final InputStream input, final InetAddress host) throws IOException { sendFile(fileName, mode, input, host, DEFAULT_PORT); } /** * Requests to send a file to a remote host, reads the file from an InputStream, sends the file to the remote host, and closes the connection. A local UDP * socket must first be created by {@link org.apache.commons.net.DatagramSocketClient#open open()} before invoking this method. This method will not close * the InputStream containing the file; you must close it after the method invocation. * * @param fileName The name the remote server should use when creating the file on its file system. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param input the input stream containing the data to be sent * @param host The remote host receiving the file. * @param port The port number of the remote TFTP server. * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. */ public void sendFile(final String fileName, final int mode, InputStream input, InetAddress host, final int port) throws IOException { int block = 0; int hostPort = 0; boolean justStarted = true; boolean lastAckWait = false; totalBytesSent = 0L; if (mode == TFTP.ASCII_MODE) { input = new ToNetASCIIInputStream(input); } TFTPPacket sent = new TFTPWriteRequestPacket(host, port, fileName, mode); final TFTPDataPacket data = new TFTPDataPacket(host, port, 0, sendBuffer, 4, 0); beginBufferedOps(); try { do { // until eof // first time: block is 0, lastBlock is 0, send a request packet. // subsequent: block is integer starting at 1, send data packet. bufferedSend(sent); boolean wantReply = true; int timeouts = 0; do { try { final TFTPPacket received = bufferedReceive(); final InetAddress recdAddress = received.getAddress(); final int recdPort = received.getPort(); // The first time we receive we get the port number and // answering host address (for hosts with multiple IPs) if (justStarted) { justStarted = false; if (recdPort == port) { // must not use the control port here final TFTPErrorPacket error = new TFTPErrorPacket(recdAddress, recdPort, TFTPErrorPacket.UNKNOWN_TID, "INCORRECT SOURCE PORT"); bufferedSend(error); throw new IOException("Incorrect source port (" + recdPort + ") in request reply."); } hostPort = recdPort; data.setPort(hostPort); if (!host.equals(recdAddress)) { host = recdAddress; data.setAddress(host); sent.setAddress(host); } } // Comply with RFC 783 indication that an error acknowledgment // should be sent to originator if unexpected TID or host. if (host.equals(recdAddress) && recdPort == hostPort) { switch (received.getType()) { case TFTPPacket.ERROR: final TFTPErrorPacket error = (TFTPErrorPacket) received; throw new IOException("Error code " + error.getError() + " received: " + error.getMessage()); case TFTPPacket.ACKNOWLEDGEMENT: final int lastBlock = ((TFTPAckPacket) received).getBlockNumber(); if (lastBlock == block) { ++block; if (block > 65535) { // wrap the block number block = 0; } wantReply = false; // got the ack we want } else { discardPackets(); } break; default: throw new IOException("Received unexpected packet type."); } } else { // wrong host or TID; send error final TFTPErrorPacket error = new TFTPErrorPacket(recdAddress, recdPort, TFTPErrorPacket.UNKNOWN_TID, "Unexpected host or port."); bufferedSend(error); } } catch (final SocketException | InterruptedIOException e) { if (++timeouts >= maxTimeouts) { throw new IOException("Connection timed out."); } } catch (final TFTPPacketException e) { throw new IOException("Bad packet: " + e.getMessage()); } // retry until a good ack } while (wantReply); if (lastAckWait) { break; // we were waiting for this; now all done } int dataLength = TFTPPacket.SEGMENT_SIZE; int offset = 4; int totalThisPacket = 0; int bytesRead = 0; while (dataLength > 0 && (bytesRead = input.read(sendBuffer, offset, dataLength)) > 0) { offset += bytesRead; dataLength -= bytesRead; totalThisPacket += bytesRead; } if (totalThisPacket < TFTPPacket.SEGMENT_SIZE) { /* this will be our last packet -- send, wait for ack, stop */ lastAckWait = true; } data.setBlockNumber(block); data.setData(sendBuffer, 4, totalThisPacket); sent = data; totalBytesSent += totalThisPacket; } while (true); // loops until after lastAckWait is set } finally { endBufferedOps(); } } /** * Same as calling sendFile(fileName, mode, input, hostname, TFTP.DEFAULT_PORT). * * @param fileName The name the remote server should use when creating the file on its file system. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param input the input stream containing the data to be sent * @param hostname The name of the remote host receiving the file. * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. * @throws UnknownHostException If the hostname cannot be resolved. */ public void sendFile(final String fileName, final int mode, final InputStream input, final String hostname) throws UnknownHostException, IOException { sendFile(fileName, mode, input, InetAddress.getByName(hostname), DEFAULT_PORT); } /** * Requests to send a file to a remote host, reads the file from an InputStream, sends the file to the remote host, and closes the connection. A local UDP * socket must first be created by {@link org.apache.commons.net.DatagramSocketClient#open open()} before invoking this method. This method will not close * the InputStream containing the file; you must close it after the method invocation. * * @param fileName The name the remote server should use when creating the file on its file system. * @param mode The TFTP mode of the transfer (one of the MODE constants). * @param input the input stream containing the data to be sent * @param hostname The name of the remote host receiving the file. * @param port The port number of the remote TFTP server. * @throws IOException If an I/O error occurs. The nature of the error will be reported in the message. * @throws UnknownHostException If the hostname cannot be resolved. */ public void sendFile(final String fileName, final int mode, final InputStream input, final String hostname, final int port) throws UnknownHostException, IOException { sendFile(fileName, mode, input, InetAddress.getByName(hostname), port); } /** * Sets the maximum number of times a receive attempt is allowed to timeout during a receiveFile() or sendFile() operation before ending attempts to retry * the receive and failing. The default is DEFAULT_MAX_TIMEOUTS. * * @param numTimeouts The maximum number of timeouts to allow. Values less than 1 should not be used, but if they are, they are treated as 1. */ public void setMaxTimeouts(final int numTimeouts) { maxTimeouts = Math.max(numTimeouts, 1); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTPDataPacket.java000066400000000000000000000177021434047722200322370ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.net.DatagramPacket; import java.net.InetAddress; /** * A final class derived from TFTPPacket definiing the TFTP Data packet type. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods. * * * @see TFTPPacket * @see TFTPPacketException * @see TFTP */ public final class TFTPDataPacket extends TFTPPacket { /** The maximum number of bytes in a TFTP data packet (512) */ public static final int MAX_DATA_LENGTH = 512; /** The minimum number of bytes in a TFTP data packet (0) */ public static final int MIN_DATA_LENGTH = 0; /** The block number of the packet. */ int blockNumber; /** The length of the data. */ private int length; /** The offset into the _data array at which the data begins. */ private int offset; /** The data stored in the packet. */ private byte[] data; /** * Creates a data packet based from a received datagram. Assumes the datagram is at least length 4, else an ArrayIndexOutOfBoundsException may be thrown. * * @param datagram The datagram containing the received data. * @throws TFTPPacketException If the datagram isn't a valid TFTP data packet. */ TFTPDataPacket(final DatagramPacket datagram) throws TFTPPacketException { super(TFTPPacket.DATA, datagram.getAddress(), datagram.getPort()); this.data = datagram.getData(); this.offset = 4; if (getType() != this.data[1]) { throw new TFTPPacketException("TFTP operator code does not match type."); } this.blockNumber = (((this.data[2] & 0xff) << 8) | (this.data[3] & 0xff)); this.length = datagram.getLength() - 4; if (this.length > MAX_DATA_LENGTH) { this.length = MAX_DATA_LENGTH; } } public TFTPDataPacket(final InetAddress destination, final int port, final int blockNumber, final byte[] data) { this(destination, port, blockNumber, data, 0, data.length); } /** * Creates a data packet to be sent to a host at a given port with a given block number. The actual data to be sent is passed as an array, an offset, and a * length. The offset is the offset into the byte array where the data starts. The length is the length of the data. If the length is greater than * MAX_DATA_LENGTH, it is truncated. * * @param destination The host to which the packet is going to be sent. * @param port The port to which the packet is going to be sent. * @param blockNumber The block number of the data. * @param data The byte array containing the data. * @param offset The offset into the array where the data starts. * @param length The length of the data. */ public TFTPDataPacket(final InetAddress destination, final int port, final int blockNumber, final byte[] data, final int offset, final int length) { super(TFTPPacket.DATA, destination, port); this.blockNumber = blockNumber; this.data = data; this.offset = offset; this.length = Math.min(length, MAX_DATA_LENGTH); } /** * Returns the block number of the data packet. * * @return The block number of the data packet. */ public int getBlockNumber() { return blockNumber; } /** * Returns the byte array containing the packet data. * * @return The byte array containing the packet data. */ public byte[] getData() { return data; } /** * Returns the length of the data part of the data packet. * * @return The length of the data part of the data packet. */ public int getDataLength() { return length; } /** * Returns the offset into the byte array where the packet data actually starts. * * @return The offset into the byte array where the packet data actually starts. */ public int getDataOffset() { return offset; } /** * Creates a UDP datagram containing all the TFTP data packet data in the proper format. This is a method exposed to the programmer in case he wants to * implement his own TFTP client instead of using the {@link org.apache.commons.net.tftp.TFTPClient} class. Under normal circumstances, you should not have * a need to call this method. * * @return A UDP datagram containing the TFTP data packet. */ @Override public DatagramPacket newDatagram() { final byte[] data; data = new byte[length + 4]; data[0] = 0; data[1] = (byte) type; data[2] = (byte) ((blockNumber & 0xffff) >> 8); data[3] = (byte) (blockNumber & 0xff); System.arraycopy(this.data, offset, data, 4, length); return new DatagramPacket(data, length + 4, address, port); } /** * This is a method only available within the package for implementing efficient datagram transport by elminating buffering. It takes a datagram as an * argument, and a byte buffer in which to store the raw datagram data. Inside the method, the data is set as the datagram's data and the datagram returned. * * @param datagram The datagram to create. * @param data The buffer to store the packet and to use in the datagram. * @return The datagram argument. */ @Override DatagramPacket newDatagram(final DatagramPacket datagram, final byte[] data) { data[0] = 0; data[1] = (byte) type; data[2] = (byte) ((blockNumber & 0xffff) >> 8); data[3] = (byte) (blockNumber & 0xff); // Doublecheck we're not the same if (data != this.data) { System.arraycopy(this.data, offset, data, 4, length); } datagram.setAddress(address); datagram.setPort(port); datagram.setData(data); datagram.setLength(length + 4); return datagram; } /** * Sets the block number of the data packet. * * @param blockNumber the number to set */ public void setBlockNumber(final int blockNumber) { this.blockNumber = blockNumber; } /** * Sets the data for the data packet. * * @param data The byte array containing the data. * @param offset The offset into the array where the data starts. * @param length The length of the data. */ public void setData(final byte[] data, final int offset, final int length) { this.data = data; this.offset = offset; this.length = length; this.length = Math.min(length, MAX_DATA_LENGTH); } /** * For debugging * * @since 3.6 */ @Override public String toString() { return super.toString() + " DATA " + blockNumber + " " + length; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTPErrorPacket.java000066400000000000000000000160531434047722200324550ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.net.DatagramPacket; import java.net.InetAddress; /** * A final class derived from TFTPPacket definiing the TFTP Error packet type. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods. * * * @see TFTPPacket * @see TFTPPacketException * @see TFTP */ public final class TFTPErrorPacket extends TFTPPacket { /** The undefined error code according to RFC 783, value 0. */ public static final int UNDEFINED = 0; /** The file not found error code according to RFC 783, value 1. */ public static final int FILE_NOT_FOUND = 1; /** The access violation error code according to RFC 783, value 2. */ public static final int ACCESS_VIOLATION = 2; /** The disk full error code according to RFC 783, value 3. */ public static final int OUT_OF_SPACE = 3; /** * The illegal TFTP operation error code according to RFC 783, value 4. */ public static final int ILLEGAL_OPERATION = 4; /** The unknown transfer id error code according to RFC 783, value 5. */ public static final int UNKNOWN_TID = 5; /** The file already exists error code according to RFC 783, value 6. */ public static final int FILE_EXISTS = 6; /** The no such user error code according to RFC 783, value 7. */ public static final int NO_SUCH_USER = 7; /** The error code of this packet. */ private final int error; /** The error message of this packet. */ private final String message; /** * Creates an error packet based from a received datagram. Assumes the datagram is at least length 4, else an ArrayIndexOutOfBoundsException may be thrown. * * @param datagram The datagram containing the received error. * @throws TFTPPacketException If the datagram isn't a valid TFTP error packet. */ TFTPErrorPacket(final DatagramPacket datagram) throws TFTPPacketException { super(TFTPPacket.ERROR, datagram.getAddress(), datagram.getPort()); int index; final int length; final byte[] data; final StringBuilder buffer; data = datagram.getData(); length = datagram.getLength(); if (getType() != data[1]) { throw new TFTPPacketException("TFTP operator code does not match type."); } error = (data[2] & 0xff) << 8 | data[3] & 0xff; if (length < 5) { throw new TFTPPacketException("Bad error packet. No message."); } index = 4; buffer = new StringBuilder(); while (index < length && data[index] != 0) { buffer.append((char) data[index]); ++index; } message = buffer.toString(); } /** * Creates an error packet to be sent to a host at a given port with an error code and error message. * * @param destination The host to which the packet is going to be sent. * @param port The port to which the packet is going to be sent. * @param error The error code of the packet. * @param message The error message of the packet. */ public TFTPErrorPacket(final InetAddress destination, final int port, final int error, final String message) { super(TFTPPacket.ERROR, destination, port); this.error = error; this.message = message; } /** * Returns the error code of the packet. * * @return The error code of the packet. */ public int getError() { return error; } /** * Returns the error message of the packet. * * @return The error message of the packet. */ public String getMessage() { return message; } /** * Creates a UDP datagram containing all the TFTP error packet data in the proper format. This is a method exposed to the programmer in case he wants to * implement his own TFTP client instead of using the {@link org.apache.commons.net.tftp.TFTPClient} class. Under normal circumstances, you should not have * a need to call this method. * * @return A UDP datagram containing the TFTP error packet. */ @Override public DatagramPacket newDatagram() { final byte[] data; final int length; length = message.length(); data = new byte[length + 5]; data[0] = 0; data[1] = (byte) type; data[2] = (byte) ((error & 0xffff) >> 8); data[3] = (byte) (error & 0xff); System.arraycopy(message.getBytes(), 0, data, 4, length); data[length + 4] = 0; return new DatagramPacket(data, data.length, address, port); } /** * This is a method only available within the package for implementing efficient datagram transport by elminating buffering. It takes a datagram as an * argument, and a byte buffer in which to store the raw datagram data. Inside the method, the data is set as the datagram's data and the datagram returned. * * @param datagram The datagram to create. * @param data The buffer to store the packet and to use in the datagram. * @return The datagram argument. */ @Override DatagramPacket newDatagram(final DatagramPacket datagram, final byte[] data) { final int length; length = message.length(); data[0] = 0; data[1] = (byte) type; data[2] = (byte) ((error & 0xffff) >> 8); data[3] = (byte) (error & 0xff); System.arraycopy(message.getBytes(), 0, data, 4, length); data[length + 4] = 0; datagram.setAddress(address); datagram.setPort(port); datagram.setData(data); datagram.setLength(length + 4); return datagram; } /** * For debugging * * @since 3.6 */ @Override public String toString() { return super.toString() + " ERR " + error + " " + message; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTPPacket.java000066400000000000000000000176731434047722200314540ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.net.DatagramPacket; import java.net.InetAddress; /** * TFTPPacket is an abstract class encapsulating the functionality common to the 5 types of TFTP packets. It also provides a static factory method that will * create the correct TFTP packet instance from a datagram. This relieves the programmer from having to figure out what kind of TFTP packet is contained in a * datagram and create it himself. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods. * * * @see TFTPPacketException * @see TFTP */ public abstract class TFTPPacket { /** * The minimum size of a packet. This is 4 bytes. It is enough to store the opcode and blocknumber or other required data depending on the packet type. */ static final int MIN_PACKET_SIZE = 4; /** * This is the actual TFTP spec identifier and is equal to 1. Identifier returned by {@link #getType getType()} indicating a read request packet. */ public static final int READ_REQUEST = 1; /** * This is the actual TFTP spec identifier and is equal to 2. Identifier returned by {@link #getType getType()} indicating a write request packet. */ public static final int WRITE_REQUEST = 2; /** * This is the actual TFTP spec identifier and is equal to 3. Identifier returned by {@link #getType getType()} indicating a data packet. */ public static final int DATA = 3; /** * This is the actual TFTP spec identifier and is equal to 4. Identifier returned by {@link #getType getType()} indicating an acknowledgement packet. */ public static final int ACKNOWLEDGEMENT = 4; /** * This is the actual TFTP spec identifier and is equal to 5. Identifier returned by {@link #getType getType()} indicating an error packet. */ public static final int ERROR = 5; /** * The TFTP data packet maximum segment size in bytes. This is 512 and is useful for those familiar with the TFTP protocol who want to use the * {@link org.apache.commons.net.tftp.TFTP} class methods to implement their own TFTP servers or clients. */ public static final int SEGMENT_SIZE = 512; /** * When you receive a datagram that you expect to be a TFTP packet, you use this factory method to create the proper TFTPPacket object encapsulating the * data contained in that datagram. This method is the only way you can instantiate a TFTPPacket derived class from a datagram. * * @param datagram The datagram containing a TFTP packet. * @return The TFTPPacket object corresponding to the datagram. * @throws TFTPPacketException If the datagram does not contain a valid TFTP packet. */ public static final TFTPPacket newTFTPPacket(final DatagramPacket datagram) throws TFTPPacketException { final byte[] data; TFTPPacket packet = null; if (datagram.getLength() < MIN_PACKET_SIZE) { throw new TFTPPacketException("Bad packet. Datagram data length is too short."); } data = datagram.getData(); switch (data[1]) { case READ_REQUEST: packet = new TFTPReadRequestPacket(datagram); break; case WRITE_REQUEST: packet = new TFTPWriteRequestPacket(datagram); break; case DATA: packet = new TFTPDataPacket(datagram); break; case ACKNOWLEDGEMENT: packet = new TFTPAckPacket(datagram); break; case ERROR: packet = new TFTPErrorPacket(datagram); break; default: throw new TFTPPacketException("Bad packet. Invalid TFTP operator code."); } return packet; } /** The type of packet. */ int type; /** The port the packet came from or is going to. */ int port; /** The host the packet is going to be sent or where it came from. */ InetAddress address; /** * This constructor is not visible outside of the package. It is used by subclasses within the package to initialize base data. * * @param type The type of the packet. * @param address The host the packet came from or is going to be sent. * @param port The port the packet came from or is going to be sent. **/ TFTPPacket(final int type, final InetAddress address, final int port) { this.type = type; this.address = address; this.port = port; } /** * Returns the address of the host where the packet is going to be sent or where it came from. * * @return The type of the packet. */ public final InetAddress getAddress() { return address; } /** * Returns the port where the packet is going to be sent or where it came from. * * @return The port where the packet came from or where it is going. */ public final int getPort() { return port; } /** * Returns the type of the packet. * * @return The type of the packet. */ public final int getType() { return type; } /** * Creates a UDP datagram containing all the TFTP packet data in the proper format. This is an abstract method, exposed to the programmer in case he wants * to implement his own TFTP client instead of using the {@link org.apache.commons.net.tftp.TFTPClient} class. Under normal circumstances, you should not * have a need to call this method. * * @return A UDP datagram containing the TFTP packet. */ public abstract DatagramPacket newDatagram(); /** * This is an abstract method only available within the package for implementing efficient datagram transport by elminating buffering. It takes a datagram * as an argument, and a byte buffer in which to store the raw datagram data. Inside the method, the data should be set as the datagram's data and the * datagram returned. * * @param datagram The datagram to create. * @param data The buffer to store the packet and to use in the datagram. * @return The datagram argument. */ abstract DatagramPacket newDatagram(DatagramPacket datagram, byte[] data); /** * Sets the host address where the packet is going to be sent. * * @param address the address to set */ public final void setAddress(final InetAddress address) { this.address = address; } /** * Sets the port where the packet is going to be sent. * * @param port the port to set */ public final void setPort(final int port) { this.port = port; } /** * For debugging * * @since 3.6 */ @Override public String toString() { return address + " " + port + " " + type; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTPPacketException.java000066400000000000000000000035401434047722200333170ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; /** * A class used to signify the occurrence of an error in the creation of a TFTP packet. It is not declared final so that it may be subclassed to identify more * specific errors. You would only want to do this if you were building your own TFTP client or server on top of the {@link org.apache.commons.net.tftp.TFTP} * class if you wanted more functionality than the {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} and * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods provide. * * * @see TFTPPacket * @see TFTP */ public class TFTPPacketException extends Exception { private static final long serialVersionUID = -8114699256840851439L; /** * Simply calls the corresponding constructor of its superclass. */ public TFTPPacketException() { } /** * Simply calls the corresponding constructor of its superclass. * * @param message the message */ public TFTPPacketException(final String message) { super(message); } } TFTPReadRequestPacket.java000066400000000000000000000061601434047722200335270ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.net.DatagramPacket; import java.net.InetAddress; /** * A class derived from TFTPRequestPacket definiing a TFTP read request packet type. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods. * * * @see TFTPPacket * @see TFTPRequestPacket * @see TFTPPacketException * @see TFTP */ public final class TFTPReadRequestPacket extends TFTPRequestPacket { /** * Creates a read request packet of based on a received datagram and assumes the datagram has already been identified as a read request. Assumes the * datagram is at least length 4, else an ArrayIndexOutOfBoundsException may be thrown. * * @param datagram The datagram containing the received request. * @throws TFTPPacketException If the datagram isn't a valid TFTP request packet. */ TFTPReadRequestPacket(final DatagramPacket datagram) throws TFTPPacketException { super(TFTPPacket.READ_REQUEST, datagram); } /** * Creates a read request packet to be sent to a host at a given port with a file name and transfer mode request. * * @param destination The host to which the packet is going to be sent. * @param port The port to which the packet is going to be sent. * @param fileName The requested file name. * @param mode The requested transfer mode. This should be on of the TFTP class MODE constants (e.g., TFTP.NETASCII_MODE). */ public TFTPReadRequestPacket(final InetAddress destination, final int port, final String fileName, final int mode) { super(destination, port, TFTPPacket.READ_REQUEST, fileName, mode); } /** * For debugging * * @since 3.6 */ @Override public String toString() { return super.toString() + " RRQ " + getFilename() + " " + TFTP.getModeName(getMode()); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/TFTPRequestPacket.java000066400000000000000000000200341434047722200330060ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.net.DatagramPacket; import java.net.InetAddress; /** * An abstract class derived from TFTPPacket definiing a TFTP Request packet type. It is subclassed by the * {@link org.apache.commons.net.tftp.TFTPReadRequestPacket} and {@link org.apache.commons.net.tftp.TFTPWriteRequestPacket} classes. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods. * * * @see TFTPPacket * @see TFTPReadRequestPacket * @see TFTPWriteRequestPacket * @see TFTPPacketException * @see TFTP */ public abstract class TFTPRequestPacket extends TFTPPacket { /** * An array containing the string names of the transfer modes and indexed by the transfer mode constants. */ static final String[] modeStrings = { "netascii", "octet" }; /** * A null terminated byte array representation of the ascii names of the transfer mode constants. This is convenient for creating the TFTP request packets. */ private static final byte[] modeBytes[] = { { (byte) 'n', (byte) 'e', (byte) 't', (byte) 'a', (byte) 's', (byte) 'c', (byte) 'i', (byte) 'i', 0 }, { (byte) 'o', (byte) 'c', (byte) 't', (byte) 'e', (byte) 't', 0 } }; /** The transfer mode of the request. */ private final int mode; /** The file name of the request. */ private final String fileName; /** * Creates a request packet of a given type to be sent to a host at a given port with a file name and transfer mode request. * * @param destination The host to which the packet is going to be sent. * @param port The port to which the packet is going to be sent. * @param type The type of the request (either TFTPPacket.READ_REQUEST or TFTPPacket.WRITE_REQUEST). * @param fileName The requested file name. * @param mode The requested transfer mode. This should be on of the TFTP class MODE constants (e.g., TFTP.NETASCII_MODE). */ TFTPRequestPacket(final InetAddress destination, final int port, final int type, final String fileName, final int mode) { super(type, destination, port); this.fileName = fileName; this.mode = mode; } /** * Creates a request packet of a given type based on a received datagram. Assumes the datagram is at least length 4, else an ArrayIndexOutOfBoundsException * may be thrown. * * @param type The type of the request (either TFTPPacket.READ_REQUEST or TFTPPacket.WRITE_REQUEST). * @param datagram The datagram containing the received request. * @throws TFTPPacketException If the datagram isn't a valid TFTP request packet of the appropriate type. */ TFTPRequestPacket(final int type, final DatagramPacket datagram) throws TFTPPacketException { super(type, datagram.getAddress(), datagram.getPort()); final byte[] data = datagram.getData(); if (getType() != data[1]) { throw new TFTPPacketException("TFTP operator code does not match type."); } final StringBuilder buffer = new StringBuilder(); int index = 2; int length = datagram.getLength(); while (index < length && data[index] != 0) { buffer.append((char) data[index]); ++index; } this.fileName = buffer.toString(); if (index >= length) { throw new TFTPPacketException("Bad file name and mode format."); } buffer.setLength(0); ++index; // need to advance beyond the end of string marker while (index < length && data[index] != 0) { buffer.append((char) data[index]); ++index; } final String modeString = buffer.toString().toLowerCase(java.util.Locale.ENGLISH); length = modeStrings.length; int mode = 0; for (index = 0; index < length; index++) { if (modeString.equals(modeStrings[index])) { mode = index; break; } } this.mode = mode; if (index >= length) { throw new TFTPPacketException("Unrecognized TFTP transfer mode: " + modeString); // May just want to default to binary mode instead of throwing // exception. // _mode = TFTP.OCTET_MODE; } } /** * Returns the requested file name. * * @return The requested file name. */ public final String getFilename() { return fileName; } /** * Returns the transfer mode of the request. * * @return The transfer mode of the request. */ public final int getMode() { return mode; } /** * Creates a UDP datagram containing all the TFTP request packet data in the proper format. This is a method exposed to the programmer in case he wants to * implement his own TFTP client instead of using the {@link org.apache.commons.net.tftp.TFTPClient} class. Under normal circumstances, you should not have * a need to call this method. * * @return A UDP datagram containing the TFTP request packet. */ @Override public final DatagramPacket newDatagram() { final int fileLength; final int modeLength; final byte[] data; fileLength = fileName.length(); modeLength = modeBytes[mode].length; data = new byte[fileLength + modeLength + 4]; data[0] = 0; data[1] = (byte) type; System.arraycopy(fileName.getBytes(), 0, data, 2, fileLength); data[fileLength + 2] = 0; System.arraycopy(modeBytes[mode], 0, data, fileLength + 3, modeLength); return new DatagramPacket(data, data.length, address, port); } /** * This is a method only available within the package for implementing efficient datagram transport by elminating buffering. It takes a datagram as an * argument, and a byte buffer in which to store the raw datagram data. Inside the method, the data is set as the datagram's data and the datagram returned. * * @param datagram The datagram to create. * @param data The buffer to store the packet and to use in the datagram. * @return The datagram argument. */ @Override final DatagramPacket newDatagram(final DatagramPacket datagram, final byte[] data) { final int fileLength; final int modeLength; fileLength = fileName.length(); modeLength = modeBytes[mode].length; data[0] = 0; data[1] = (byte) type; System.arraycopy(fileName.getBytes(), 0, data, 2, fileLength); data[fileLength + 2] = 0; System.arraycopy(modeBytes[mode], 0, data, fileLength + 3, modeLength); datagram.setAddress(address); datagram.setPort(port); datagram.setData(data); datagram.setLength(fileLength + modeLength + 3); return datagram; } } TFTPWriteRequestPacket.java000066400000000000000000000061711434047722200337500ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.net.DatagramPacket; import java.net.InetAddress; /** * A class derived from TFTPRequestPacket definiing a TFTP write request packet type. *

* Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods. * * * @see TFTPPacket * @see TFTPRequestPacket * @see TFTPPacketException * @see TFTP */ public final class TFTPWriteRequestPacket extends TFTPRequestPacket { /** * Creates a write request packet of based on a received datagram and assumes the datagram has already been identified as a write request. Assumes the * datagram is at least length 4, else an ArrayIndexOutOfBoundsException may be thrown. * * @param datagram The datagram containing the received request. * @throws TFTPPacketException If the datagram isn't a valid TFTP request packet. */ TFTPWriteRequestPacket(final DatagramPacket datagram) throws TFTPPacketException { super(TFTPPacket.WRITE_REQUEST, datagram); } /** * Creates a write request packet to be sent to a host at a given port with a file name and transfer mode request. * * @param destination The host to which the packet is going to be sent. * @param port The port to which the packet is going to be sent. * @param fileName The requested file name. * @param mode The requested transfer mode. This should be on of the TFTP class MODE constants (e.g., TFTP.NETASCII_MODE). */ public TFTPWriteRequestPacket(final InetAddress destination, final int port, final String fileName, final int mode) { super(destination, port, TFTPPacket.WRITE_REQUEST, fileName, mode); } /** * For debugging * * @since 3.6 */ @Override public String toString() { return super.toString() + " WRQ " + getFilename() + " " + TFTP.getModeName(getMode()); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/tftp/package-info.java000066400000000000000000000015561434047722200320640ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * TFTP cliemt implementation */ package org.apache.commons.net.tftp;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/time/000077500000000000000000000000001434047722200266475ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/time/TimeTCPClient.java000066400000000000000000000074621434047722200321270ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.time; import java.io.DataInputStream; import java.io.IOException; import java.util.Date; import org.apache.commons.net.SocketClient; /** * The TimeTCPClient class is a TCP implementation of a client for the Time protocol described in RFC 868. To use the class, merely establish a connection with * {@link org.apache.commons.net.SocketClient#connect connect } and call either {@link #getTime getTime() } or {@link #getDate getDate() } to retrieve the time, * then call {@link org.apache.commons.net.SocketClient#disconnect disconnect } to close the connection properly. * * * @see TimeUDPClient */ public final class TimeTCPClient extends SocketClient { /** The default time port. It is set to 37 according to RFC 868. */ public static final int DEFAULT_PORT = 37; /** * The number of seconds between 00:00 1 January 1900 and 00:00 1 January 1970. This value can be useful for converting time values to other formats. */ public static final long SECONDS_1900_TO_1970 = 2208988800L; /** * The default TimeTCPClient constructor. It merely sets the default port to DEFAULT_PORT . */ public TimeTCPClient() { setDefaultPort(DEFAULT_PORT); } /** * Retrieves the time from the server and returns a Java Date containing the time converted to the local time zone. *

* The server will have closed the connection at this point, so you should call {@link org.apache.commons.net.SocketClient#disconnect disconnect } after * calling this method. To retrieve another time, you must initiate another connection with {@link org.apache.commons.net.SocketClient#connect connect } * before calling getDate() again. * * @return A Date value containing the time retrieved from the server converted to the local time zone. * @throws IOException If an error occurs while fetching the time. */ public Date getDate() throws IOException { return new Date((getTime() - SECONDS_1900_TO_1970) * 1000L); } /** * Retrieves the time from the server and returns it. The time is the number of seconds since 00:00 (midnight) 1 January 1900 GMT, as specified by RFC 868. * This method reads the raw 32-bit big-endian unsigned integer from the server, converts it to a Java long, and returns the value. *

* The server will have closed the connection at this point, so you should call {@link org.apache.commons.net.SocketClient#disconnect disconnect } after * calling this method. To retrieve another time, you must initiate another connection with {@link org.apache.commons.net.SocketClient#connect connect } * before calling getTime() again. * * @return The time value retrieved from the server. * @throws IOException If an error occurs while fetching the time. */ public long getTime() throws IOException { final DataInputStream input; input = new DataInputStream(_input_); return input.readInt() & 0xffffffffL; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/time/TimeUDPClient.java000066400000000000000000000112601434047722200321200ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.time; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.util.Date; import org.apache.commons.net.DatagramSocketClient; /** * The TimeUDPClient class is a UDP implementation of a client for the Time protocol described in RFC 868. To use the class, merely open a local datagram socket * with {@link org.apache.commons.net.DatagramSocketClient#open open } and call {@link #getTime getTime } or {@link #getTime getDate } to retrieve the time. * Then call {@link org.apache.commons.net.DatagramSocketClient#close close } to close the connection properly. Unlike * {@link org.apache.commons.net.time.TimeTCPClient}, successive calls to {@link #getTime getTime } or {@link #getDate getDate } are permitted without * re-establishing a connection. That is because UDP is a connectionless protocol and the Time protocol is stateless. * * * @see TimeTCPClient */ public final class TimeUDPClient extends DatagramSocketClient { /** The default time port. It is set to 37 according to RFC 868. */ public static final int DEFAULT_PORT = 37; /** * The number of seconds between 00:00 1 January 1900 and 00:00 1 January 1970. This value can be useful for converting time values to other formats. */ public static final long SECONDS_1900_TO_1970 = 2208988800L; private final byte[] dummyData = new byte[1]; private final byte[] timeData = new byte[4]; /** * Same as getTime(host, DEFAULT_PORT); * * @param host the time server * @return the date * @throws IOException on error */ public Date getDate(final InetAddress host) throws IOException { return new Date((getTime(host, DEFAULT_PORT) - SECONDS_1900_TO_1970) * 1000L); } /** * Retrieves the time from the server and returns a Java Date containing the time converted to the local time zone. * * @param host The address of the server. * @param port The port of the service. * @return A Date value containing the time retrieved from the server converted to the local time zone. * @throws IOException If an error occurs while fetching the time. */ public Date getDate(final InetAddress host, final int port) throws IOException { return new Date((getTime(host, port) - SECONDS_1900_TO_1970) * 1000L); } /** * Same as getTime(host, DEFAULT_PORT); * * @param host the time server * @return the time returned from the server * @throws IOException on error */ public long getTime(final InetAddress host) throws IOException { return getTime(host, DEFAULT_PORT); } /** * Retrieves the time from the specified server and port and returns it. The time is the number of seconds since 00:00 (midnight) 1 January 1900 GMT, as * specified by RFC 868. This method reads the raw 32-bit big-endian unsigned integer from the server, converts it to a Java long, and returns the value. * * @param host The address of the server. * @param port The port of the service. * @return The time value retrieved from the server. * @throws IOException If an error occurs while retrieving the time. */ public long getTime(final InetAddress host, final int port) throws IOException { long time; final DatagramPacket sendPacket; final DatagramPacket receivePacket; sendPacket = new DatagramPacket(dummyData, dummyData.length, host, port); receivePacket = new DatagramPacket(timeData, timeData.length); _socket_.send(sendPacket); _socket_.receive(receivePacket); time = 0L; time |= (((timeData[0] & 0xff) << 24) & 0xffffffffL); time |= (((timeData[1] & 0xff) << 16) & 0xffffffffL); time |= (((timeData[2] & 0xff) << 8) & 0xffffffffL); time |= ((timeData[3] & 0xff) & 0xffffffffL); return time; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/time/package-info.java000066400000000000000000000015741434047722200320450ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Time protocol (RFC 868) over TCP and UDP */ package org.apache.commons.net.time;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/000077500000000000000000000000001434047722200266665ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/Base64.java000066400000000000000000001121561434047722200305630ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.util; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.Objects; /** * Provides Base64 encoding and decoding as defined by RFC 2045. * *

* This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: * Format of Internet Message Bodies by Freed and Borenstein. *

*

* The class can be parameterized in the following manner with various constructors: *

    *
  • URL-safe mode: Default off.
  • *
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. *
  • Line separator: Default is CRLF ("\r\n")
  • *
*

* Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode character encodings which are * compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). *

* * @see RFC 2045 * @since 2.2 */ public class Base64 { private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; private static final int DEFAULT_BUFFER_SIZE = 8192; /** * Chunk size per RFC 2045 section 6.8. * *

* The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any equal signs. *

* * @see RFC 2045 section 6.8 */ static final int CHUNK_SIZE = 76; /** * Chunk separator per RFC 2045 section 2.1. * * @see RFC 2045 section 2.1 */ private static final byte[] CHUNK_SEPARATOR = { '\r', '\n' }; /** * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" equivalents as specified in Table 1 of RFC * 2045. * * Thanks to "commons" project in ws.apache.org for this code. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ */ private static final byte[] STANDARD_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; /** * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed to - and _ to make the encoded Base64 results more URL-SAFE. This table is * only used when the Base64's mode is set to URL-SAFE. */ private static final byte[] URL_SAFE_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; /** * Byte used to pad output. */ private static final byte PAD = '='; /** * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into their 6-bit * positive integer equivalents. Characters that are not in the Base64 alphabet but fall within the bounds of the array are translated to -1. * * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both URL_SAFE and STANDARD base64. (The * encoder, on the other hand, needs to know ahead of time what to emit). * * Thanks to "commons" project in ws.apache.org for this code. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ */ private static final byte[] DECODE_TABLE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; /** Mask used to extract 6 bits, used when encoding */ private static final int MASK_6BITS = 0x3f; /** Mask used to extract 8 bits, used in decoding base64 bytes */ private static final int MASK_8BITS = 0xff; // The static final fields above are used for the original static byte[] methods on Base64. // The private member fields below are used with the new streaming approach, which requires // some state be preserved between calls of encode() and decode(). /** * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. * * @param arrayOctet byte array to test * @return true if any byte is a valid character in the Base64 alphabet; false herwise */ private static boolean containsBase64Byte(final byte[] arrayOctet) { for (final byte element : arrayOctet) { if (isBase64(element)) { return true; } } return false; } /** * Decodes Base64 data into octets. * * @param base64Data Byte array containing Base64 data * @return Array containing decoded data. */ public static byte[] decodeBase64(final byte[] base64Data) { return new Base64().decode(base64Data); } /** * Decodes a Base64 String into octets. * * @param base64String String containing Base64 data * @return Array containing decoded data. * @since 1.4 */ public static byte[] decodeBase64(final String base64String) { return new Base64().decode(base64String); } // Implementation of integer encoding used for crypto /** * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature * * @param pArray a byte array containing base64 character data * @return A BigInteger * @since 1.4 */ public static BigInteger decodeInteger(final byte[] pArray) { return new BigInteger(1, decodeBase64(pArray)); } /** * Encodes binary data using the base64 algorithm but does not chunk the output. * * @param binaryData binary data to encode * @return byte[] containing Base64 characters in their UTF-8 representation. */ public static byte[] encodeBase64(final byte[] binaryData) { return encodeBase64(binaryData, false); } /** * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. * * @param binaryData Array containing binary data to encode. * @param isChunked if true this encoder will chunk the base64 output into 76 character blocks * @return Base64-encoded data. * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} */ public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) { return encodeBase64(binaryData, isChunked, false); } /** * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. * * @param binaryData Array containing binary data to encode. * @param isChunked if true this encoder will chunk the base64 output into 76 character blocks * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / characters. * @return Base64-encoded data. * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} * @since 1.4 */ public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) { return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); } /** * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. * * @param binaryData Array containing binary data to encode. * @param isChunked if true this encoder will chunk the base64 output into 76 character blocks * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / characters. * @param maxResultSize The maximum result size to accept. * @return Base64-encoded data. * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than maxResultSize * @since 1.4 */ public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe, final int maxResultSize) { if (binaryData == null || binaryData.length == 0) { return binaryData; } final long len = getEncodeLength(binaryData, isChunked ? CHUNK_SIZE : 0, isChunked ? CHUNK_SEPARATOR : NetConstants.EMPTY_BTYE_ARRAY); if (len > maxResultSize) { throw new IllegalArgumentException( "Input array too big, the output array would be bigger (" + len + ") than the specified maxium size of " + maxResultSize); } final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); return b64.encode(binaryData); } /** * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks * * @param binaryData binary data to encode * @return Base64 characters chunked in 76 character blocks */ public static byte[] encodeBase64Chunked(final byte[] binaryData) { return encodeBase64(binaryData, true); } /** * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF. *

* For a non-chunking version, see {@link #encodeBase64StringUnChunked(byte[])}. * * @param binaryData binary data to encode * @return String containing Base64 characters. * @since 1.4 */ public static String encodeBase64String(final byte[] binaryData) { return newStringUtf8(encodeBase64(binaryData, true)); } /** * Encodes binary data using the base64 algorithm. * * @param binaryData binary data to encode * @param useChunking whether to split the output into chunks * @return String containing Base64 characters. * @since 3.2 */ public static String encodeBase64String(final byte[] binaryData, final boolean useChunking) { return newStringUtf8(encodeBase64(binaryData, useChunking)); } /** * Encodes binary data using the base64 algorithm, without using chunking. *

* For a chunking version, see {@link #encodeBase64String(byte[])}. * * @param binaryData binary data to encode * @return String containing Base64 characters. * @since 3.2 */ public static String encodeBase64StringUnChunked(final byte[] binaryData) { return newStringUtf8(encodeBase64(binaryData, false)); } /** * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The url-safe variation emits - and _ instead of + * and / characters. * * @param binaryData binary data to encode * @return byte[] containing Base64 characters in their UTF-8 representation. * @since 1.4 */ public static byte[] encodeBase64URLSafe(final byte[] binaryData) { return encodeBase64(binaryData, false, true); } /** * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The url-safe variation emits - and _ instead of + * and / characters. * * @param binaryData binary data to encode * @return String containing Base64 characters * @since 1.4 */ public static String encodeBase64URLSafeString(final byte[] binaryData) { return newStringUtf8(encodeBase64(binaryData, false, true)); } /** * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature * * @param bigInt a BigInteger * @return A byte array containing base64 character data * @throws NullPointerException if null is passed in * @since 1.4 */ public static byte[] encodeInteger(final BigInteger bigInt) { return encodeBase64(toIntegerBytes(bigInt), false); } /** * Pre-calculates the amount of space needed to base64-encode the supplied array. * * @param pArray byte[] array which will later be encoded * @param chunkSize line-length of the output (<= 0 means no chunking) between each chunkSeparator (e.g. CRLF). * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF). * * @return amount of space needed to encoded the supplied array. Returns a long since a max-len array will require Integer.MAX_VALUE + 33%. */ private static long getEncodeLength(final byte[] pArray, int chunkSize, final byte[] chunkSeparator) { // base64 always encodes to multiples of 4. chunkSize = (chunkSize / 4) * 4; long len = (pArray.length * 4) / 3; final long mod = len % 4; if (mod != 0) { len += 4 - mod; } if (chunkSize > 0) { final boolean lenChunksPerfectly = len % chunkSize == 0; len += (len / chunkSize) * chunkSeparator.length; if (!lenChunksPerfectly) { len += chunkSeparator.length; } } return len; } /** * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the method treats whitespace as valid. * * @param arrayOctet byte array to test * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; false, otherwise */ public static boolean isArrayByteBase64(final byte[] arrayOctet) { for (final byte element : arrayOctet) { if (!isBase64(element) && !isWhiteSpace(element)) { return false; } } return true; } /** * Returns whether or not the octet is in the base 64 alphabet. * * @param octet The value to test * @return true if the value is defined in the the base 64 alphabet, false otherwise. * @since 1.4 */ public static boolean isBase64(final byte octet) { return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); } /** * Checks if a byte value is whitespace or not. * * @param byteToCheck the byte to check * @return true if byte is whitespace, false otherwise */ private static boolean isWhiteSpace(final byte byteToCheck) { switch (byteToCheck) { case ' ': case '\n': case '\r': case '\t': return true; default: return false; } } private static String newStringUtf8(final byte[] encode) { return new String(encode, StandardCharsets.UTF_8); } /** * Returns a byte-array representation of a BigInteger without sign bit. * * @param bigInt BigInteger to be converted * @return a byte array representation of the BigInteger parameter */ static byte[] toIntegerBytes(final BigInteger bigInt) { Objects.requireNonNull(bigInt, "bigInt"); int bitlen = bigInt.bitLength(); // round bitlen bitlen = ((bitlen + 7) >> 3) << 3; final byte[] bigBytes = bigInt.toByteArray(); if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { return bigBytes; } // set up params for copying everything but sign bit int startSrc = 0; int len = bigBytes.length; // if bigInt is exactly byte-aligned, just skip signbit in copy if ((bigInt.bitLength() % 8) == 0) { startSrc = 1; len--; } final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec final byte[] resizedBytes = new byte[bitlen / 8]; System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); return resizedBytes; } /** * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able to decode both STANDARD and URL_SAFE * streams, but the encodeTable must be a member variable so we can switch between the two modes. */ private final byte[] encodeTable; /** * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the base64 encoded data. */ private final int lineLength; /** * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. */ private final byte[] lineSeparator; /** * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. * decodeSize = 3 + lineSeparator.length; */ private final int decodeSize; /** * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. * encodeSize = 4 + lineSeparator.length; */ private final int encodeSize; /** * Buffer for streaming. */ private byte[] buffer; /** * Position where next character should be written in the buffer. */ private int pos; /** * Position where next character should be read from the buffer. */ private int readPos; /** * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to make sure each encoded line never goes * beyond lineLength (if lineLength > 0). */ private int currentLinePos; /** * Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when decoding. This variable helps track that. */ private int modulus; /** * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 object becomes useless, and must be thrown away. */ private boolean eof; /** * Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store and extract the base64 encoding or decoding from this * variable. */ private int x; /** * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. *

* When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. *

* *

* When decoding all variants are supported. *

*/ public Base64() { this(false); } /** * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. *

* When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. *

* *

* When decoding all variants are supported. *

* * @param urlSafe if true, URL-safe encoding is used. In most cases this should be set to false. * @since 1.4 */ public Base64(final boolean urlSafe) { this(CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); } /** * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. *

* When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. *

*

* Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. *

*

* When decoding all variants are supported. *

* * @param lineLength Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). If {@code lineLength <= 0}, then * the output will not be divided into lines (chunks). Ignored when decoding. * @since 1.4 */ public Base64(final int lineLength) { this(lineLength, CHUNK_SEPARATOR); } /** * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. *

* When encoding the line length and line separator are given in the constructor, and the encoding table is STANDARD_ENCODE_TABLE. *

*

* Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. *

*

* When decoding all variants are supported. *

* * @param lineLength Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). If {@code lineLength <= 0}, * then the output will not be divided into lines (chunks). Ignored when decoding. * @param lineSeparator Each line of encoded data will end with this sequence of bytes. * @throws IllegalArgumentException Thrown when the provided lineSeparator included some base64 characters. * @since 1.4 */ public Base64(final int lineLength, final byte[] lineSeparator) { this(lineLength, lineSeparator, false); } /** * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. *

* When encoding the line length and line separator are given in the constructor, and the encoding table is STANDARD_ENCODE_TABLE. *

*

* Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. *

*

* When decoding all variants are supported. *

* * @param lineLength Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). If {@code lineLength <= 0}, * then the output will not be divided into lines (chunks). Ignored when decoding. * @param lineSeparator Each line of encoded data will end with this sequence of bytes. * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode operations. Decoding seamlessly * handles both modes. * @throws IllegalArgumentException The provided lineSeparator included some base64 characters. That's not going to work! * @since 1.4 */ public Base64(int lineLength, byte[] lineSeparator, final boolean urlSafe) { if (lineSeparator == null) { lineLength = 0; // disable chunk-separating lineSeparator = NetConstants.EMPTY_BTYE_ARRAY; // this just gets ignored } this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0; this.lineSeparator = new byte[lineSeparator.length]; System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); if (lineLength > 0) { this.encodeSize = 4 + lineSeparator.length; } else { this.encodeSize = 4; } this.decodeSize = this.encodeSize - 1; if (containsBase64Byte(lineSeparator)) { final String sep = newStringUtf8(lineSeparator); throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]"); } this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; } /** * Returns the amount of buffered data available for reading. * * @return The amount of buffered data available for reading. */ int avail() { return buffer != null ? pos - readPos : 0; } /** * Decodes a byte[] containing containing characters in the Base64 alphabet. * * @param pArray A byte array containing Base64 character data * @return a byte array containing binary data */ public byte[] decode(final byte[] pArray) { reset(); if (pArray == null || pArray.length == 0) { return pArray; } final long len = (pArray.length * 3) / 4; final byte[] buf = new byte[(int) len]; setInitialBuffer(buf, 0, buf.length); decode(pArray, 0, pArray.length); decode(pArray, 0, -1); // Notify decoder of EOF. // Would be nice to just return buf (like we sometimes do in the encode // logic), but we have no idea what the line-length was (could even be // variable). So we cannot determine ahead of time exactly how big an // array is necessary. Hence the need to construct a 2nd byte array to // hold the final result: final byte[] result = new byte[pos]; readResults(result, 0, result.length); return result; } /** *

* Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once with the data to decode, and once with * inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" call is not necessary when decoding, but it doesn't hurt, either. *

*

* Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are silently ignored, but has implications * for other bytes, too. This method subscribes to the garbage-in, garbage-out philosophy: it will not check the provided data for validity. *

*

* Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ *

* * @param in byte[] array of ascii data to base64 decode. * @param inPos Position to start reading data from. * @param inAvail Amount of bytes available from input for encoding. */ void decode(final byte[] in, int inPos, final int inAvail) { if (eof) { return; } if (inAvail < 0) { eof = true; } for (int i = 0; i < inAvail; i++) { if (buffer == null || buffer.length - pos < decodeSize) { resizeBuffer(); } final byte b = in[inPos++]; if (b == PAD) { // We're done. eof = true; break; } if (b >= 0 && b < DECODE_TABLE.length) { final int result = DECODE_TABLE[b]; if (result >= 0) { modulus = (++modulus) % 4; x = (x << 6) + result; if (modulus == 0) { buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); buffer[pos++] = (byte) (x & MASK_8BITS); } } } } // Two forms of EOF as far as base64 decoder is concerned: actual // EOF (-1) and first time '=' character is encountered in stream. // This approach makes the '=' padding characters completely optional. if (eof && modulus != 0) { x = x << 6; switch (modulus) { case 2: x = x << 6; buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); break; case 3: buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); break; default: break; // other values ignored } } } /** * Decodes a String containing containing characters in the Base64 alphabet. * * @param pArray A String containing Base64 character data * @return a byte array containing binary data * @since 1.4 */ public byte[] decode(final String pArray) { return decode(getBytesUtf8(pArray)); } /** * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet. * * @param pArray a byte array containing binary data * @return A byte array containing only Base64 character data */ public byte[] encode(final byte[] pArray) { reset(); if (pArray == null || pArray.length == 0) { return pArray; } final long len = getEncodeLength(pArray, lineLength, lineSeparator); byte[] buf = new byte[(int) len]; setInitialBuffer(buf, 0, buf.length); encode(pArray, 0, pArray.length); encode(pArray, 0, -1); // Notify encoder of EOF. // Encoder might have resized, even though it was unnecessary. if (buffer != buf) { readResults(buf, 0, buf.length); } // In URL-SAFE mode we skip the padding characters, so sometimes our // final length is a bit smaller. if (isUrlSafe() && pos < buf.length) { final byte[] smallerBuf = new byte[pos]; System.arraycopy(buf, 0, smallerBuf, 0, pos); buf = smallerBuf; } return buf; } /** *

* Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with the data to encode, and once with * inAvail set to "-1" to alert encoder that EOF has been reached, so flush last remaining bytes (if not multiple of 3). *

*

* Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ *

* * @param in byte[] array of binary data to base64 encode. * @param inPos Position to start reading data from. * @param inAvail Amount of bytes available from input for encoding. */ void encode(final byte[] in, int inPos, final int inAvail) { if (eof) { return; } // inAvail < 0 is how we're informed of EOF in the underlying data we're // encoding. if (inAvail < 0) { eof = true; if (buffer == null || buffer.length - pos < encodeSize) { resizeBuffer(); } switch (modulus) { case 1: buffer[pos++] = encodeTable[(x >> 2) & MASK_6BITS]; buffer[pos++] = encodeTable[(x << 4) & MASK_6BITS]; // URL-SAFE skips the padding to further reduce size. if (encodeTable == STANDARD_ENCODE_TABLE) { buffer[pos++] = PAD; buffer[pos++] = PAD; } break; case 2: buffer[pos++] = encodeTable[(x >> 10) & MASK_6BITS]; buffer[pos++] = encodeTable[(x >> 4) & MASK_6BITS]; buffer[pos++] = encodeTable[(x << 2) & MASK_6BITS]; // URL-SAFE skips the padding to further reduce size. if (encodeTable == STANDARD_ENCODE_TABLE) { buffer[pos++] = PAD; } break; default: break; // other values ignored } if (lineLength > 0 && pos > 0) { System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); pos += lineSeparator.length; } } else { for (int i = 0; i < inAvail; i++) { if (buffer == null || buffer.length - pos < encodeSize) { resizeBuffer(); } modulus = (++modulus) % 3; int b = in[inPos++]; if (b < 0) { b += 256; } x = (x << 8) + b; if (0 == modulus) { buffer[pos++] = encodeTable[(x >> 18) & MASK_6BITS]; buffer[pos++] = encodeTable[(x >> 12) & MASK_6BITS]; buffer[pos++] = encodeTable[(x >> 6) & MASK_6BITS]; buffer[pos++] = encodeTable[x & MASK_6BITS]; currentLinePos += 4; if (lineLength > 0 && lineLength <= currentLinePos) { System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); pos += lineSeparator.length; currentLinePos = 0; } } } } } /** * Encodes a byte[] containing binary data, into a String containing characters in the Base64 alphabet. * * @param pArray a byte array containing binary data * @return A String containing only Base64 character data * @since 1.4 */ public String encodeToString(final byte[] pArray) { return newStringUtf8(encode(pArray)); } private byte[] getBytesUtf8(final String pArray) { return pArray.getBytes(StandardCharsets.UTF_8); } int getLineLength() { return lineLength; } byte[] getLineSeparator() { return lineSeparator.clone(); } /** * Returns true if this Base64 object has buffered data for reading. * * @return true if there is Base64 object still available for reading. */ boolean hasData() { return this.buffer != null; } /** * Returns our current encode mode. True if we're URL-SAFE, false otherwise. * * @return true if we're in URL-SAFE mode, false otherwise. * @since 1.4 */ public boolean isUrlSafe() { return this.encodeTable == URL_SAFE_ENCODE_TABLE; } /** * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail bytes. Returns how many bytes were actually * extracted. * * @param b byte[] array to extract the buffered data into. * @param bPos position in byte[] array to start extraction at. * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). * @return The number of bytes successfully extracted into the provided byte[] array. */ int readResults(final byte[] b, final int bPos, final int bAvail) { if (buffer != null) { final int len = Math.min(avail(), bAvail); if (buffer != b) { System.arraycopy(buffer, readPos, b, bPos, len); readPos += len; if (readPos >= pos) { buffer = null; } } else { // Re-using the original consumer's output array is only // allowed for one round. buffer = null; } return len; } return eof ? -1 : 0; } /** * Resets this Base64 object to its initial newly constructed state. */ private void reset() { buffer = null; pos = 0; readPos = 0; currentLinePos = 0; modulus = 0; eof = false; } // Getters for use in testing /** Doubles our buffer. */ private void resizeBuffer() { if (buffer == null) { buffer = new byte[DEFAULT_BUFFER_SIZE]; pos = 0; readPos = 0; } else { final byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; System.arraycopy(buffer, 0, b, 0, buffer.length); buffer = b; } } /** * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the consumer's output array for one round (if the consumer * calls this method first) instead of starting our own buffer. * * @param out byte[] array to buffer directly to. * @param outPos Position to start buffering into. * @param outAvail Amount of bytes available for direct buffering. */ void setInitialBuffer(final byte[] out, final int outPos, final int outAvail) { // We can re-use consumer's original output array under // special circumstances, saving on some System.arraycopy(). if (out != null && out.length == outAvail) { buffer = out; pos = outPos; readPos = outPos; } } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/Charsets.java000066400000000000000000000037131434047722200313110ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.util; import java.nio.charset.Charset; /** * Helps dealing with Charsets. * * @since 3.3 */ public class Charsets { /** * Returns a charset object for the given charset name. * * @param charsetName The name of the requested charset; may be a canonical name, an alias, or null. If null, return the default charset. * @return A charset object for the named charset */ public static Charset toCharset(final String charsetName) { return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName); } /** * Returns a charset object for the given charset name. * * @param charsetName The name of the requested charset; may be a canonical name, an alias, or null. If null, return the default charset. * @param defaultCharsetName the charset name to use if the requested charset is null * * @return A charset object for the named charset */ public static Charset toCharset(final String charsetName, final String defaultCharsetName) { return charsetName == null ? Charset.forName(defaultCharsetName) : Charset.forName(charsetName); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/KeyManagerUtils.java000066400000000000000000000217131434047722200326010ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.Socket; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.Principal; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Enumeration; import javax.net.ssl.KeyManager; import javax.net.ssl.X509ExtendedKeyManager; import org.apache.commons.net.io.Util; /** * General KeyManager utilities *

* How to use with a client certificate: * *

 * KeyManager km = KeyManagerUtils.createClientKeyManager("JKS",
 *     "/path/to/privatekeystore.jks","storepassword",
 *     "privatekeyalias", "keypassword");
 * FTPSClient cl = new FTPSClient();
 * cl.setKeyManager(km);
 * cl.connect(...);
 * 
* * If using the default store type and the key password is the same as the store password, these parameters can be omitted.
* If the desired key is the first or only key in the keystore, the keyAlias parameter can be omitted, in which case the code becomes: * *
 * KeyManager km = KeyManagerUtils.createClientKeyManager(
 *     "/path/to/privatekeystore.jks","storepassword");
 * FTPSClient cl = new FTPSClient();
 * cl.setKeyManager(km);
 * cl.connect(...);
 * 
* * @since 3.0 */ public final class KeyManagerUtils { private static class ClientKeyStore { private final X509Certificate[] certChain; private final PrivateKey key; private final String keyAlias; ClientKeyStore(final KeyStore ks, final String keyAlias, final String keyPass) throws GeneralSecurityException { this.keyAlias = keyAlias; this.key = (PrivateKey) ks.getKey(this.keyAlias, keyPass.toCharArray()); final Certificate[] certs = ks.getCertificateChain(this.keyAlias); final X509Certificate[] x509certs = new X509Certificate[certs.length]; Arrays.setAll(x509certs, i -> (X509Certificate) certs[i]); this.certChain = x509certs; } final String getAlias() { return this.keyAlias; } final X509Certificate[] getCertificateChain() { return this.certChain; } final PrivateKey getPrivateKey() { return this.key; } } private static class X509KeyManager extends X509ExtendedKeyManager { private final ClientKeyStore keyStore; X509KeyManager(final ClientKeyStore keyStore) { this.keyStore = keyStore; } // Call sequence: 1 @Override public String chooseClientAlias(final String[] keyType, final Principal[] issuers, final Socket socket) { return keyStore.getAlias(); } @Override public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) { return null; } // Call sequence: 2 @Override public X509Certificate[] getCertificateChain(final String alias) { return keyStore.getCertificateChain(); } @Override public String[] getClientAliases(final String keyType, final Principal[] issuers) { return new String[] { keyStore.getAlias() }; } // Call sequence: 3 @Override public PrivateKey getPrivateKey(final String alias) { return keyStore.getPrivateKey(); } @Override public String[] getServerAliases(final String keyType, final Principal[] issuers) { return null; } } private static final String DEFAULT_STORE_TYPE = KeyStore.getDefaultType(); /** * Create a client key manager which returns a particular key. Does not handle server keys. Uses the default store type and assumes the key password is the * same as the store password. The key alias is found by searching the keystore for the first private key entry * * @param storePath the path to the keyStore * @param storePass the keyStore password * @return the customised KeyManager * @throws IOException if there is a problem creating the keystore * @throws GeneralSecurityException if there is a problem creating the keystore */ public static KeyManager createClientKeyManager(final File storePath, final String storePass) throws IOException, GeneralSecurityException { return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, null, storePass); } /** * Create a client key manager which returns a particular key. Does not handle server keys. Uses the default store type and assumes the key password is the * same as the store password * * @param storePath the path to the keyStore * @param storePass the keyStore password * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used * @return the customised KeyManager * @throws IOException if there is a problem creating the keystore * @throws GeneralSecurityException if there is a problem creating the keystore */ public static KeyManager createClientKeyManager(final File storePath, final String storePass, final String keyAlias) throws IOException, GeneralSecurityException { return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, keyAlias, storePass); } /** * Create a client key manager which returns a particular key. Does not handle server keys. * * @param ks the keystore to use * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used * @param keyPass the password of the key to use * @return the customised KeyManager * @throws GeneralSecurityException if there is a problem creating the keystore */ public static KeyManager createClientKeyManager(final KeyStore ks, final String keyAlias, final String keyPass) throws GeneralSecurityException { final ClientKeyStore cks = new ClientKeyStore(ks, keyAlias != null ? keyAlias : findAlias(ks), keyPass); return new X509KeyManager(cks); } /** * Create a client key manager which returns a particular key. Does not handle server keys. * * @param storeType the type of the keyStore, e.g. "JKS" * @param storePath the path to the keyStore * @param storePass the keyStore password * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used * @param keyPass the password of the key to use * @return the customised KeyManager * @throws GeneralSecurityException if there is a problem creating the keystore * @throws IOException if there is a problem creating the keystore */ public static KeyManager createClientKeyManager(final String storeType, final File storePath, final String storePass, final String keyAlias, final String keyPass) throws IOException, GeneralSecurityException { final KeyStore ks = loadStore(storeType, storePath, storePass); return createClientKeyManager(ks, keyAlias, keyPass); } private static String findAlias(final KeyStore ks) throws KeyStoreException { final Enumeration e = ks.aliases(); while (e.hasMoreElements()) { final String entry = e.nextElement(); if (ks.isKeyEntry(entry)) { return entry; } } throw new KeyStoreException("Cannot find a private key entry"); } private static KeyStore loadStore(final String storeType, final File storePath, final String storePass) throws KeyStoreException, IOException, GeneralSecurityException { final KeyStore ks = KeyStore.getInstance(storeType); FileInputStream stream = null; try { stream = new FileInputStream(storePath); ks.load(stream, storePass.toCharArray()); } finally { Util.closeQuietly(stream); } return ks; } private KeyManagerUtils() { // Not instantiable } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/ListenerList.java000066400000000000000000000046151434047722200321600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.util; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.EventListener; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** */ public class ListenerList implements Serializable, Iterable { private static final long serialVersionUID = -1934227607974228213L; private final CopyOnWriteArrayList listeners; public ListenerList() { listeners = new CopyOnWriteArrayList<>(); } public void addListener(final EventListener listener) { listeners.add(listener); } public int getListenerCount() { return listeners.size(); } /** * Return an {@link Iterator} for the {@link EventListener} instances. * * @return an {@link Iterator} for the {@link EventListener} instances * @since 2.0 TODO Check that this is a good defensive strategy */ @Override public Iterator iterator() { return listeners.iterator(); } private void readObject(final ObjectInputStream in) { throw new UnsupportedOperationException("Serialization is not supported"); } /* * Serialization is unnecessary for this class. Reject attempts to do so until such time as the Serializable attribute can be dropped. */ public void removeListener(final EventListener listener) { listeners.remove(listener); } private void writeObject(final ObjectOutputStream out) { throw new UnsupportedOperationException("Serialization is not supported"); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/NetConstants.java000066400000000000000000000032201434047722200321510ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.util; import java.security.cert.X509Certificate; /** * Constants provided as public only for our own implementation, you can consider this private for now. * * @since 3.8.0 */ public class NetConstants { /** * An empty immutable {@code String} array. */ public static final String[] EMPTY_STRING_ARRAY = {}; /** * An empty immutable {@code byte} array. */ public static final byte[] EMPTY_BTYE_ARRAY = {}; /** * An empty immutable {link X509Certificate} array. */ public static final X509Certificate[] EMPTY_X509_CERTIFICATE_ARRAY = {}; /** * The index value when the end of the stream has been reached {@code -1}. * * @since 3.9.0 */ public static final int EOS = -1; /** * Prevents instantiation. */ private NetConstants() { } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/SSLContextUtils.java000066400000000000000000000057001434047722200325620ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.util; import java.io.IOException; import java.security.GeneralSecurityException; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; /** * General utilities for SSLContext. * * @since 3.0 */ public class SSLContextUtils { /** * Create and initialize an SSLContext. * * @param protocol the protocol used to instatiate the context * @param keyManager the key manager, may be {@code null} * @param trustManager the trust manager, may be {@code null} * @return the initialized context. * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs */ public static SSLContext createSSLContext(final String protocol, final KeyManager keyManager, final TrustManager trustManager) throws IOException { return createSSLContext(protocol, keyManager == null ? null : new KeyManager[] { keyManager }, trustManager == null ? null : new TrustManager[] { trustManager }); } /** * Create and initialize an SSLContext. * * @param protocol the protocol used to instatiate the context * @param keyManagers the array of key managers, may be {@code null} but array entries must not be {@code null} * @param trustManagers the array of trust managers, may be {@code null} but array entries must not be {@code null} * @return the initialized context. * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs */ public static SSLContext createSSLContext(final String protocol, final KeyManager[] keyManagers, final TrustManager[] trustManagers) throws IOException { final SSLContext ctx; try { ctx = SSLContext.getInstance(protocol); ctx.init(keyManagers, trustManagers, /* SecureRandom */ null); } catch (final GeneralSecurityException e) { final IOException ioe = new IOException("Could not initialize SSL context"); ioe.initCause(e); throw ioe; } return ctx; } private SSLContextUtils() { // Not instantiable } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/SSLSocketUtils.java000066400000000000000000000031251434047722200323650ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.util; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; /** * General utilities for SSLSocket. * * @since 3.4 */ public class SSLSocketUtils { /** * Enable the HTTPS endpoint identification algorithm on an SSLSocket. * * @param socket the SSL socket * @return {@code true} on success */ public static boolean enableEndpointNameVerification(final SSLSocket socket) { final SSLParameters sslParameters = socket.getSSLParameters(); if (sslParameters != null) { sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); socket.setSSLParameters(sslParameters); return true; } return false; } private SSLSocketUtils() { // Not instantiable } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/SubnetUtils.java000066400000000000000000000332651434047722200320230ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.util; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Performs some subnet calculations given a network address and a subnet mask. * * @see "http://www.faqs.org/rfcs/rfc1519.html" * @since 2.0 */ public class SubnetUtils { /** * Convenience container for subnet summary information. */ public final class SubnetInfo { /** Mask to convert unsigned int to a long (i.e. keep 32 bits). */ private static final long UNSIGNED_INT_MASK = 0x0FFFFFFFFL; private SubnetInfo() { } public int asInteger(final String address) { return toInteger(address); } private long broadcastLong() { return broadcast & UNSIGNED_INT_MASK; } /** * Converts a 4-element array into dotted decimal format. */ private String format(final int[] octets) { final int last = octets.length - 1; final StringBuilder builder = new StringBuilder(); for (int i = 0;; i++) { builder.append(octets[i]); if (i == last) { return builder.toString(); } builder.append('.'); } } public String getAddress() { return format(toArray(address)); } /** * Gets the count of available addresses. Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. * * @return the count of addresses, may be zero. * @throws RuntimeException if the correct count is greater than {@code Integer.MAX_VALUE} * @deprecated (3.4) use {@link #getAddressCountLong()} instead */ @Deprecated public int getAddressCount() { final long countLong = getAddressCountLong(); if (countLong > Integer.MAX_VALUE) { throw new RuntimeException("Count is larger than an integer: " + countLong); } // Cannot be negative here return (int) countLong; } /** * Gets the count of available addresses. Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. * * @return the count of addresses, may be zero. * @since 3.4 */ public long getAddressCountLong() { final long b = broadcastLong(); final long n = networkLong(); final long count = b - n + (isInclusiveHostCount() ? 1 : -1); return count < 0 ? 0 : count; } public String[] getAllAddresses() { final int ct = getAddressCount(); final String[] addresses = new String[ct]; if (ct == 0) { return addresses; } for (int add = low(), j = 0; add <= high(); ++add, ++j) { addresses[j] = format(toArray(add)); } return addresses; } public String getBroadcastAddress() { return format(toArray(broadcast)); } public String getCidrSignature() { return format(toArray(address)) + "/" + Integer.bitCount(netmask); } /** * Gets the high address as a dotted IP address. Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. * * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address */ public String getHighAddress() { return format(toArray(high())); } /** * Gets the low address as a dotted IP address. Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. * * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address */ public String getLowAddress() { return format(toArray(low())); } public String getNetmask() { return format(toArray(netmask)); } public String getNetworkAddress() { return format(toArray(network)); } public String getNextAddress() { return format(toArray(address + 1)); } public String getPreviousAddress() { return format(toArray(address - 1)); } private int high() { return isInclusiveHostCount() ? broadcast : broadcastLong() - networkLong() > 1 ? broadcast - 1 : 0; } /** * Tests if the parameter address is in the range of usable endpoint addresses for this subnet. This excludes the network and broadcast * addresses by default. Use {@link SubnetUtils#setInclusiveHostCount(boolean)} to change this. * * @param address the address to check * @return true if it is in range * @since 3.4 (made public) */ public boolean isInRange(final int address) { if (address == 0) { // cannot ever be in range; rejecting now avoids problems with CIDR/31,32 return false; } final long addLong = address & UNSIGNED_INT_MASK; final long lowLong = low() & UNSIGNED_INT_MASK; final long highLong = high() & UNSIGNED_INT_MASK; return addLong >= lowLong && addLong <= highLong; } /** * Tests if the parameter address is in the range of usable endpoint addresses for this subnet. This excludes the network and broadcast * addresses. Use {@link SubnetUtils#setInclusiveHostCount(boolean)} to change this. * * @param address A dot-delimited IPv4 address, e.g. "192.168.0.1" * @return True if in range, false otherwise */ public boolean isInRange(final String address) { return isInRange(toInteger(address)); } private int low() { return isInclusiveHostCount() ? network : broadcastLong() - networkLong() > 1 ? network + 1 : 0; } /** long versions of the values (as unsigned int) which are more suitable for range checking. */ private long networkLong() { return network & UNSIGNED_INT_MASK; } /** * Converts a packed integer address into a 4-element array */ private int[] toArray(final int val) { final int ret[] = new int[4]; for (int j = 3; j >= 0; --j) { ret[j] |= val >>> 8 * (3 - j) & 0xff; } return ret; } /** * {@inheritDoc} * * @since 2.2 */ @Override public String toString() { final StringBuilder buf = new StringBuilder(); // @formatter:off buf.append("CIDR Signature:\t[").append(getCidrSignature()).append("]\n") .append(" Netmask: [").append(getNetmask()).append("]\n") .append(" Network: [").append(getNetworkAddress()).append("]\n") .append(" Broadcast: [").append(getBroadcastAddress()).append("]\n") .append(" First address: [").append(getLowAddress()).append("]\n") .append(" Last address: [").append(getHighAddress()).append("]\n") .append(" Address Count: [").append(getAddressCountLong()).append("]\n"); // @formatter:on return buf.toString(); } } private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})"; private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,2})"; // 0 -> 32 private static final Pattern ADDRESS_PATTERN = Pattern.compile(IP_ADDRESS); private static final Pattern CIDR_PATTERN = Pattern.compile(SLASH_FORMAT); private static final int NBITS = 32; private static final String PARSE_FAIL = "Could not parse [%s]"; /* * Extracts the components of a dotted decimal address and pack into an integer using a regex match */ private static int matchAddress(final Matcher matcher) { int addr = 0; for (int i = 1; i <= 4; ++i) { final int n = rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255); addr |= (n & 0xff) << 8 * (4 - i); } return addr; } /* * Checks integer boundaries. Checks if a value x is in the range [begin,end]. Returns x if it is in range, throws an exception otherwise. */ private static int rangeCheck(final int value, final int begin, final int end) { if (value >= begin && value <= end) { // (begin,end] return value; } throw new IllegalArgumentException("Value [" + value + "] not in range [" + begin + "," + end + "]"); } /* * Converts a dotted decimal format address to a packed integer format */ private static int toInteger(final String address) { final Matcher matcher = ADDRESS_PATTERN.matcher(address); if (matcher.matches()) { return matchAddress(matcher); } throw new IllegalArgumentException(String.format(PARSE_FAIL, address)); } private final int netmask; private final int address; private final int network; private final int broadcast; /** Whether the broadcast/network address are included in host count */ private boolean inclusiveHostCount; /** * Constructs an instance from a CIDR-notation string, e.g. "192.168.0.1/16" * * @param cidrNotation A CIDR-notation string, e.g. "192.168.0.1/16" * @throws IllegalArgumentException if the parameter is invalid, i.e. does not match n.n.n.n/m where n=1-3 decimal digits, m = 1-2 decimal digits in range * 0-32 */ public SubnetUtils(final String cidrNotation) { final Matcher matcher = CIDR_PATTERN.matcher(cidrNotation); if (!matcher.matches()) { throw new IllegalArgumentException(String.format(PARSE_FAIL, cidrNotation)); } this.address = matchAddress(matcher); // Create a binary netmask from the number of bits specification /x final int trailingZeroes = NBITS - rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS); // // An IPv4 netmask consists of 32 bits, a contiguous sequence // of the specified number of ones followed by all zeros. // So, it can be obtained by shifting an unsigned integer (32 bits) to the left by // the number of trailing zeros which is (32 - the # bits specification). // Note that there is no unsigned left shift operator, so we have to use // a long to ensure that the left-most bit is shifted out correctly. // this.netmask = (int) (0x0FFFFFFFFL << trailingZeroes); // Calculate base network address this.network = address & netmask; // Calculate broadcast address this.broadcast = network | ~netmask; } /** * Constructs an instance from a dotted decimal address and a dotted decimal mask. * * @param address An IP address, e.g. "192.168.0.1" * @param mask A dotted decimal netmask e.g. "255.255.0.0" * @throws IllegalArgumentException if the address or mask is invalid, i.e. does not match n.n.n.n where n=1-3 decimal digits and the mask is not all zeros */ public SubnetUtils(final String address, final String mask) { this.address = toInteger(address); this.netmask = toInteger(mask); if ((this.netmask & -this.netmask) - 1 != ~this.netmask) { throw new IllegalArgumentException(String.format(PARSE_FAIL, mask)); } // Calculate base network address this.network = this.address & this.netmask; // Calculate broadcast address this.broadcast = this.network | ~this.netmask; } /** * Gets a {@link SubnetInfo} instance that contains subnet-specific statistics * * @return new instance */ public final SubnetInfo getInfo() { return new SubnetInfo(); } public SubnetUtils getNext() { return new SubnetUtils(getInfo().getNextAddress(), getInfo().getNetmask()); } public SubnetUtils getPrevious() { return new SubnetUtils(getInfo().getPreviousAddress(), getInfo().getNetmask()); } /** * Tests if the return value of {@link SubnetInfo#getAddressCount()} includes the network and broadcast addresses. * * @since 2.2 * @return true if the host count includes the network and broadcast addresses */ public boolean isInclusiveHostCount() { return inclusiveHostCount; } /** * Sets to true if you want the return value of {@link SubnetInfo#getAddressCount()} to include the network and broadcast addresses. This also * applies to {@link SubnetInfo#isInRange(int)} * * @param inclusiveHostCount true if network and broadcast addresses are to be included * @since 2.2 */ public void setInclusiveHostCount(final boolean inclusiveHostCount) { this.inclusiveHostCount = inclusiveHostCount; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/TrustManagerUtils.java000066400000000000000000000074751434047722200332030ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.util; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** * TrustManager utilities for generating TrustManagers. * * @since 3.0 */ public final class TrustManagerUtils { private static class TrustManager implements X509TrustManager { private final boolean checkServerValidity; TrustManager(final boolean checkServerValidity) { this.checkServerValidity = checkServerValidity; } /** * Never generates a CertificateException. */ @Override public void checkClientTrusted(final X509Certificate[] certificates, final String authType) { } @Override public void checkServerTrusted(final X509Certificate[] certificates, final String authType) throws CertificateException { if (checkServerValidity) { for (final X509Certificate certificate : certificates) { certificate.checkValidity(); } } } /** * @return an empty array of certificates */ @Override public X509Certificate[] getAcceptedIssuers() { return NetConstants.EMPTY_X509_CERTIFICATE_ARRAY; } } private static final X509TrustManager ACCEPT_ALL = new TrustManager(false); private static final X509TrustManager CHECK_SERVER_VALIDITY = new TrustManager(true); /** * Generate a TrustManager that performs no checks. * * @return the TrustManager */ public static X509TrustManager getAcceptAllTrustManager() { return ACCEPT_ALL; } /** * Return the default TrustManager provided by the JVM. *

* This should be the same as the default used by * {@link javax.net.ssl.SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) SSLContext#init(KeyManager[], * TrustManager[], SecureRandom)} when the TrustManager parameter is set to {@code null} * * @param keyStore the KeyStore to use, may be {@code null} * @return the default TrustManager * @throws GeneralSecurityException if an error occurs */ public static X509TrustManager getDefaultTrustManager(final KeyStore keyStore) throws GeneralSecurityException { final String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); final TrustManagerFactory instance = TrustManagerFactory.getInstance(defaultAlgorithm); instance.init(keyStore); return (X509TrustManager) instance.getTrustManagers()[0]; } /** * Generate a TrustManager that checks server certificates for validity, but otherwise performs no checks. * * @return the validating TrustManager */ public static X509TrustManager getValidateServerCertificateTrustManager() { return CHECK_SERVER_VALIDITY; } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/util/package-info.java000066400000000000000000000015431434047722200320600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Utility classes */ package org.apache.commons.net.util;commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/whois/000077500000000000000000000000001434047722200270425ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/whois/WhoisClient.java000066400000000000000000000067101434047722200321410ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.whois; import java.io.IOException; import java.io.InputStream; import org.apache.commons.net.finger.FingerClient; /** * The WhoisClient class implements the client side of the Internet Whois Protocol defined in RFC 954. To query a host you create a WhoisClient instance, * connect to the host, query the host, and finally disconnect from the host. If the whois service you want to query is on a non-standard port, connect to the * host at that port. Here's a sample use: * *

 * WhoisClient whois;
 *
 * whois = new WhoisClient();
 *
 * try {
 *     whois.connect(WhoisClient.DEFAULT_HOST);
 *     System.out.println(whois.query("foobar"));
 *     whois.disconnect();
 * } catch (IOException e) {
 *     System.err.println("Error I/O exception: " + e.getMessage());
 *     return;
 * }
 * 
* * * */ public final class WhoisClient extends FingerClient { /** * The default whois host to query. It is set to whois.internic.net. */ public static final String DEFAULT_HOST = "whois.internic.net"; /** * The default whois port. It is set to 43 according to RFC 954. */ public static final int DEFAULT_PORT = 43; /** * The default whois constructor. Initializes the default port to DEFAULT_PORT . */ public WhoisClient() { setDefaultPort(DEFAULT_PORT); } /** * Queries the connected whois server for information regarding the given handle and returns the InputStream of the network connection. It is up to the * programmer to be familiar with the handle syntax of the whois server. You must first connect to a finger server before calling this method, and you * should disconnect after finishing reading the stream. * * @param handle The handle to lookup. * @return The InputStream of the network connection of the whois query. Can be read to obtain whois results. * @throws IOException If an I/O error occurs during the operation. */ public InputStream getInputStream(final String handle) throws IOException { return getInputStream(false, handle); } /** * Queries the connected whois server for information regarding the given handle. It is up to the programmer to be familiar with the handle syntax of the * whois server. You must first connect to a whois server before calling this method, and you should disconnect afterward. * * @param handle The handle to lookup. * @return The result of the whois query. * @throws IOException If an I/O error occurs during the operation. */ public String query(final String handle) throws IOException { return query(false, handle); } } commons-net-rel-commons-net-3.9.0/src/main/java/org/apache/commons/net/whois/package-info.java000066400000000000000000000015411434047722200322320ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Whois client */ package org.apache.commons.net.whois;commons-net-rel-commons-net-3.9.0/src/main/resources/000077500000000000000000000000001434047722200225315ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/resources/org/000077500000000000000000000000001434047722200233205ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/resources/org/apache/000077500000000000000000000000001434047722200245415ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/resources/org/apache/commons/000077500000000000000000000000001434047722200262145ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/resources/org/apache/commons/net/000077500000000000000000000000001434047722200270025ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/resources/org/apache/commons/net/examples/000077500000000000000000000000001434047722200306205ustar00rootroot00000000000000examples.properties000066400000000000000000000063241434047722200345020ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/main/resources/org/apache/commons/net/examples################################################################################ # Apache Commons Net Examples Property file ################################################################################ ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. # List of aliases for example class names. # alias full class name SubnetUtilsExample org.apache.commons.net.examples.cidr.SubnetUtilsExample FTPClientExample org.apache.commons.net.examples.ftp.FTPClientExample ServerToServerFTP org.apache.commons.net.examples.ftp.ServerToServerFTP TFTPExample org.apache.commons.net.examples.ftp.TFTPExample IMAPExportMbox org.apache.commons.net.examples.mail.IMAPExportMbox IMAPImportMbox org.apache.commons.net.examples.mail.IMAPImportMbox IMAPMail org.apache.commons.net.examples.mail.IMAPMail POP3ExportMbox org.apache.commons.net.examples.mail.POP3ExportMbox POP3Mail org.apache.commons.net.examples.mail.POP3Mail SMTPMail org.apache.commons.net.examples.mail.SMTPMail ArticleReader org.apache.commons.net.examples.nntp.ArticleReader ExtendedNNTPOps org.apache.commons.net.examples.nntp.ExtendedNNTPOps ListNewsgroups org.apache.commons.net.examples.nntp.ListNewsgroups MessageThreading org.apache.commons.net.examples.nntp.MessageThreading PostMessage org.apache.commons.net.examples.nntp.PostMessage NTPClient org.apache.commons.net.examples.ntp.NTPClient SimpleNTPServer org.apache.commons.net.examples.ntp.SimpleNTPServer TimeClient org.apache.commons.net.examples.ntp.TimeClient TelnetClientExample org.apache.commons.net.examples.telnet.TelnetClientExample WeatherTelnet org.apache.commons.net.examples.telnet.WeatherTelnet chargen org.apache.commons.net.examples.unix.chargen daytime org.apache.commons.net.examples.unix.daytime echo org.apache.commons.net.examples.unix.echo finger org.apache.commons.net.examples.unix.finger fwhois org.apache.commons.net.examples.unix.fwhois rdate org.apache.commons.net.examples.unix.rdate rexec org.apache.commons.net.examples.unix.rexec rlogin org.apache.commons.net.examples.unix.rlogin rshell org.apache.commons.net.examples.unix.rshell commons-net-rel-commons-net-3.9.0/src/media/000077500000000000000000000000001434047722200206525ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/media/net-logo-white.xcf000066400000000000000000000471631434047722200242310ustar00rootroot00000000000000gimp xcf fileQBBLK gimp-commentCreated with The GIMPgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) 0&E#mL TM     CL8gimp-text-layer(text "TM") (font "Sans") (font-size 8.000000) (font-size-unit pixels) (hinting yes) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode fixed) (box-width 12.000000) (box-height 11.000000) (box-unit pixels)   #ɰ->8x88w8P8?84net     &J44HHH-U080ƪ/U.-qU8 8qUqUUU8UUUqU8qUqq88UqUUUUUUUUUUUUUU8 8U8U8U88UqUU8qqUUqU8U888q8R8Drop-Shadow#2     KR89R863.              !##"   #&)+,+*'#  !!  !"#%(+.1331-( "$&'())('&#!!#$%&'(*+-0369;;:84. !%),.0112210.,)&$!  !#$&(*+-./01358;>ABCB>:4 "',04689::98641.,)(''()*,.02356789;=?BEGIIGD>8  '-37;>?@AA@><96410//023579:<=>?@ACFHKMONLHB; $,29>BDFGGHGECA><987789:<=?@BCDEFGIKMPRSRPKE=  (07>CGIKKLLKJHFCA@??@ABCDEFGHIJKLNQSUUTRMF> "*3:AGKMNNOPONLJHGFFGHIJJKLLMOPRUVWVSNG? $,5=DJMOPPQRSTSSRPONMMLLKLMNOQSUVWVSNG> %.6?FKNPQPPQSTVWWVUUTSSRRQPOMLLKKLNPRTUVURMF> %.7?FLOPQPOOPQSUWYYZYYXXWWVTSQOMKJJIIJKMPRTUTQLE= &.7@FLOPPONNPRUXZ[\]]\\[YXURPMKIGFFGHKMPRSSPKD< &/7?FKNONMLLMNQTXZ\^__^\ZWTPMJGEDCCDEHKNPRQOKD< %.7?EJMMKJJKMPSWZ\^_`aa``^\YUQMIFCA@@ABEHKNPPNJD< %.6>DIKLKJHHIKNQUY\^_`aa_]ZVRMIEB?>=>?BEILNNMIC< $-5=CGJJIHFFGILPTWZ\^_`aa`^[WSNIEA><;;=?BFILMLHC< $,4;AFHHGFDDGJMQUXZ\]^__][XSOJEA>;:9:=@DGJKJGB< #+3:?CEFECBABDGJNRTVXYZ[\]]\ZWTOKFA>:988:=ADGIHFA; !)07=@BBA@?>?ACGJMPRSTUWXYYXVSOJFA=97667:=ADEEC?: '.49=>?><;;=?BEHJLMNOPRSTUTSQMIE@<854347:=@BB@=8 $*058::987678:=@BDEFGHIKMNOOMJFB>:63100258;<=<94  &,034432123579;=>>?@ACEFHIHGEB>:62/-,,-02577640 "'*-..-,,-/124566778:<>?@A@><951-*('&(),.010.+ !$&'('&&%&'()+,--./1246788752/+(%"!!"#%')*)(%  !!  !"#$%%&()+-.//.-*(%" ""! !"$%&&%#                             # (! ,% 0(  3*" 4+# 5,# 5,# 5," 4+" 4*! 3*! 3*! 3*" 3+" 4+# 4+# 4,$ 3+# 1*# /(! +% &! !        h)commons     #U#a!&$!8qU8Uq8q8q q UU88UU8qUqUUUqUUUqUUq8U8 UU 8q8U8 q   8U 8U UU UU UU 8UU U 8 88 U q 8888 q8 q8qUUUUUqU UUUU U 8qUq q8UqUU8qU 8q8U8 8UqU8qqq88qqUUqUUUqƪUqUUUU8Uqq q8UUU 88UUqq UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU qUq qUqU8qU88qU8UƍUUUU8q qq q8  8qUU88qU 88 8UU UUUU8U8UUUUUUUUU8q 8UqUUU UUUUU 8UUUU UUUUU UUUU UUUU UUUUUU UUUUUU UUUUU UUUUUU UUUUU8 UUUU qUUUUq UUUUq8UUUUUUUUqUqU88UUqqU8q8qUU 8Uq8 q 8VVV UU ⪪88888Uqqq88  q 8 qU  qU qq8qƪq888UUk 58 Drop-Shadow     9$8$1LxL8$M.9JD!U                    !#$%%$# !"##$%%$"   "$&')*+,,+*'%"!#$&'())*+,,*(&#! !$&(*+-.01221.,)&#! !#$'(*+,--./0234431.,)  #'),-/01346787642/,)(''(*,./11223568:;;9752 !&*-/12334679:;<;9742/..0245677665668:=?ABCB@=: "',02456678:<=>>=;97544579;<==<:9878:>=;:99:<>ABCCB@=;988:=@EIMOPONK &-38;==<:8655679;<=>==>@BEGIIHFC?;9779<@FKPSUVTR $+29=@AA>;853223579:;<==?ADGKMOOMJE@;7556:?EKQVZ[ZX  (08>CEED@<830..024689;=@CGLPSTSQLGA;63237=CKRX\__] $,5=CGJIGB=72.+))*,.1369<@EJOTWXWTOHA:40/04:AJRY^aba '09AHLNMID>71+(%%'),/26:@FLRW[\[WQIA93.,-17?HQY_cdd  )3=ELPRPLF?70*%"!!"$'*.38?FMTZ^_^YSJA92-*+/5=FOX_dfe ",6@HOTUSOIA81*$!!#'+06>ENU\`a`[TKB91,))-3;DNW^cff #-8BKSWYWRLC;2+% !$(.55-'" #'-471,)&%&'*.3:AJRZ_bc`ZSJA93/-/28@HOUY[[ !+6ALV]bdd`[TME>83/,*)*+.38?GOV\`a_ZTLC<610149@FMRUWW (3>IS[aeec_ZSLE?:52/--/27=DKRX\]\YSME>94336:?EJOQRR %/:EOX_ceeb^XRLFA<741/./149?FMSWYYWSMF@;7557;?CGKLML !+5@JS[`dec`\WQLFA<741/./15:@FLQTUSPLGB=9778;>ADFGGF &0:DMU\`bba]YTOID?:51.-,.04:?EJMOOMJFA=:88:<>@AA@? !*3=FNUZ]_^\YUPJE?:50-*))+.27=AEHIHFC@=:87789:;;:98 $-5>FMRVYYXVRNIC>83.*'%$%(+059=@BBA?=:8755421 &-5=DINPRQPMID?:4/*%" !$(,048:;;:875321100/.,+* %,3:?CFHIHEB>94/*%! $(,/13310/.-,+*('%$# $*059<>?><:62.)$  #&)+,,+*)('&%#"  !&+.134431.*&"!#$%%$$#"!  !$')**)'%"                    "                              !!"#$$%%$#"!  !"##! !!"#$%!$&'))*+,-.//.-,*))()*+,,*(%!"$&())**+,-. #&*-/123345789987642112345531-*&" #'*-012334567'%%&(+/369;<==<<=>@ACCDCB@><::;=>>=:62.)&$$%'+/37:;<=<<=>@/--.037<@CEFFEDDFGIKMMKIGECAABCDFGFEC?:51-+*,.27<@CEFFEDDEFH86558;@DILNOOMLKJJLNPSUVVTROMJHGGIJLMNMKGB=841125:?DILNONMKJJKLN@>==?CHLQTVWVTRPOOPRUX[\]\YVSPMLLMOQSTTRNID?:778;@FKPTVVUSQOOQSHEDDFJNSX[]][YVSRQRUX\_aba_\XTQONOQTVXXWTPJE@=<=@EKQWZ\\ZXUSQQSVOLKJLOTY]`ba_\XUSRSUY^adfec_[VRPOPRUX[\[YUOJEA@ADJPV[_``^ZWTRRTWUSQPQTY]adedb^YURQRUY^bfhgea\WSONOQUY\^^\XSNHECDHMSY_bcb`\WTQQSV[XUU;X\`dggfc^YTPOPSW]bfhhfb\WRNLMPTX\_`_[VQLHFGJOU\aded`[WRPOQT_\YXY[^bfhhfb]WRNLMPU[`ehhfb\VPLJKNRW\_a`]YSNJHHKPW]befd`[UPMLNRb_\[[]`dgiifb\UPKIJMRY_dggea[TNJHHKPU[_ba_ZUPKIJLQW]beec_YSNJIKOca^\\^adgiheaZTMIGGJPV]befd`YSLHEFINTZ_ab`\VQLJJMRX]beeb^WQKHGHLda_]]^adghhe`YRKGDEHMT[`dec^XQKFCDGLRY^ab`\WQMJJMRW]bdda\VOIFDFJda_]]^adghgd^XPJEBCFLRY_cdb]WPIDBBEKQX^aba]XRNKKMRW]adc`[UNHDBDHc`^\\^`cfgfc^WOICAAEJQX^bca\VOHCAADJPW]aba]XSNKKMRW]acc`ZTMFBABG`^\[[]_cegfc]VNHC@@DJQX^aba\UNHB@@DIPW]aba^YSOLLNRW]acc_ZSLFB@BF][YXY[^befeb\UNGB?@CIPW]ab`\UNGB@@CIPW]aba^YSOLLNRW\`cb_YRKEA@AEZXVVWY]`deea\UNGB?@CIPW]ab`[UNGB@@CIOW]aba^YTOLLNRW\`bb_YRKEA?AEUTRRTW[_bdda[UMGB?@CIPW\`a`[UNGB@@CIOV\`ba^YTOMLNRW\`ba^YRKEA?AEPONNPTX]acb_[TMFB?@CHOV\_`_ZTMGB@@CHOV\`a`]YTPMMNRW\_a`]XQJEA?AEJIIJLPUZ^``^YSLFA??CHNU[^_^YTMGB@@CHNU[_`_\XSOMLNRV[^`_\WQJD@?ADD2EHMRW[^^\WQKEA>?BGMSY\]\XRLFA??BGMSY]^][WRNLLMQUY]^]ZUOIC@>@D>=>@CHMSWZZXTNHC?==@EKQVYZXUOJD@>>AEKQVY[ZXTPLJIKNRVY[ZWRLFA>=>B778:>CHNRUUTPKE@=;;>BHMRUVTQLGB><<>CHMRUWVTPLIGGHKOSUVVSOID?<;CHLOPNKGB>:88;>CHLOPPNKHECCDFJMOPPMID?;878;))+.27BFHIHEA=96446:>BFHJIGEB?>>?ADGIIGC?:64347"#$'+059=?@?<952/..037;>@A@>:731//147;>@AA@=;9878:=?AA?<841/./1!$(-146641.+)((*,/2578752/,*))*-035788753100134688630-*)()+!%(+-.-+)'$#""#%(*-./.,*(%$##$&(+-.//.-+*))+,.//-+)&$""$!#$%$#!  #$%&%$" !#$&&%$#"!!"#$%&&%#!                         9 ,                     %%$#"!  !""##"    !"#//.,+*)(()*+,,+)&# !"#$%%$#!"$')+,,89987532112245542/+'" "$%&'()*+,--,+*(&#"!!"$'*.13566BCDCCA?=;::;<=>><84/*&" "%')*+,-./01234420.,*))*-037;=?@@JLMMLKIFDBAABCEFGFDA=83.)&%%&(*,.0112234689;<<;975321358=AEGIJJQSUVUTQNLIHGHIKMNNLJE@;61.,,-/24567665679<>ABCCB@><::;>AFJNQSSVY[\\[XUROMLLMORSTSQMHC>964468:;==<;9889;>BEHJKJIGECBCFINRVY[[ZZ]`bba^ZVRPNNPRUWYYWTOJEA><=>@ACCB@>;9889;?CHLOQQPOMKJKMQUY]`aa_[_cefeb^YUQOOQSWZ\]\ZVQLHFDEFGIIGD@=9778;?DIOSVWWVTRQRTW[_cefebZ_dghgd`ZUQONPSV[^``^[WSOMLLMOPONKGB=86458=CIPUZ\]\ZYWWY\_cghigdY^cghhe`ZUPMLNQUZ^acb_\XUSRSTUVURNIC=74236;AIPW\`aa`^\\]`cfikjhdW]bfhhe`ZTOKJLOTY_bdec`][YXYZ[[ZVQJC<61/028?GOW^bdedb``acehklkhcT[`eggd_XRMIHIMRX^cfgfda_^^=`a`^ZTLD<50--055.*(*.5>HQZaeggfeddegijjhc]LSZ_bb_ZSLFA@AFLS[bgijjhggijkkid]TJ@70+)+/5>GPX_ceedcbbdfhiigb\LSY_bb_YSLEA@AELS[afijihffhijjid^ULB92-+,/5=FNV\`bba``bdghhfb\KRY^aa^YRKEA?AEKSZafhihfeddeghigd^VMD<50-.16=EMTY]^^]]^`ceggea[KRY^aa^YRKEA@AEKSZ`eghfdbaabdefeb^WOF>830037=DKQVXZYYXYZ]`cefd`ZKQX]``]XRKEA@AEKRY_dffdb_^]^_abb`\WPHA:63358>CINRTTUWZ]addc_ZJQW\__\WQKEA@AEJQX^bcca^[YXYZ\]^]ZUOIC=86569=BGJMNONNPRVZ^aba^XIOUZ]]ZVPJDA?@DIPV[_``]ZWTSRSUWXXVSNIC>:878:=@DFHIHHJMRV[^_^[WGMRWYYWSMHB?>?BGMSX[\[XUQNLLNPQRQOLGC?;9889;>@ABBABDHLQVY[ZXSDJOSUUSOJE@=<=@DJOSVWVSOKHEDEFHIKKJGDA>;97789:;<<;::<>BGLPTVVSO@EIMOOMJE@<989<@EIMPPOLHD@><<=?ABCCB@>;976556543357;@EJMOOMJ;?CGHIGD@;85457;?CFHIGDA=9654568:;<;:9754221100/.-,+,.049>BFHHFC48?@><-14677641.+*))+.13677530,)&%$$%'(*+,,+*)('&%$"!  "&*.256764&)+-./.,)'%##$&)+-..,*(%" "#$%%$#"! #&),-.-+!#$%%#" !#$%%$" !#$%$#                        !   *                  #$$%$"  -.-,)&"  678753/+'"!#$%&&%$#! @?@AA@?<84/*%" !#%(*,--,+*(&# IHGGHIIGEA<71,)&%&'*-/1344321/-*'# QPNNOPPOMJE?94/-,,.1479:;::8641.*&! WUSRRSTUVVTQLF@;632247;>@AA?=;851-(# \YVUUWYZ[ZWRMGA<978:=ADFHHGEB?<840*% _[XVUVX[]^^\XRLFA><=?BFJLNNMKHD@<72,'! _[WUTUX[^`a_\VPJEB@ACGKNQSSRPMID@:5/(" _ZVSRSVZ^aba^YTMHDCCFINRUWXWURNID>81*$ ^XSPOQTX]acc`\VPJFDEGKPTXZ\[YWSNHB<4-& \VQNMNRV\`cca]WQKGEEGKPUY\^^][WSMG@91)" ZTNKJKOTZ_bcb^XRLGEDFJOTY]`aa_\XSLE=5-% YRLIGIMSY^bcb^XRLGDCDHMRW\_bcb`\XRKC:1)  WPJGFGLQX^bcb^XRKFBABEJOUZ^acdc`\WPH?6-$ VOIEDFJPW]acb^XQKEA?@BFKQV[_bddc`[UMD;1( UNHDCEJPV]acb^XQJD@==?CGMRW\`bdcb^XQH?5+" UNHDCEIPV\acb^XQJD?<;;:;=@EINSX\_aa_[UMD:0& TMGDCEIOV\`bb^XQJD>;99;>AEJNSW[]^]ZUNE;1( SMGDCEIOU[`ba^XQJD>;98:<>BFJNRVYZZXSMD;1( RLGCCDINUZ_a`]WQJD>;989::8789:@CFIJJIE@:2*" JEA?>?CHMRUWVSOJD?;75445678:<>@BCCB?:4-& EA=;:BFHJIGC?;630/../0123455430,'" 95311247;>@AA?<840.+*))*+,--..-+)%! 1.,**+-025787630-*'%$$%&'&%$! )'%$$&(*-./.-+(&#!  ! !"$%&%$#!                    z=,d Background     M7,dMWNON[Ng,dMMMMMMNNN/N?:        &>>Q            00002K% commons-net-rel-commons-net-3.9.0/src/site/000077500000000000000000000000001434047722200205375ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/site/custom/000077500000000000000000000000001434047722200220515ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/site/custom/project-info-report.properties000066400000000000000000000023361434047722200301030ustar00rootroot00000000000000# # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Replace SCM report description in index page report.scm.description=This document lists ways to access the online source repository. # Replace SCM report text for viewvc section report.scm.webaccess.title=Browser (HTTP) Access report.scm.webaccess.url=The following is a link to a browsable version of the source repository. \ Please use the "Anonymous Access" or "Developer Access" URL if you want to check out the source using your SCM client.commons-net-rel-commons-net-3.9.0/src/site/resources/000077500000000000000000000000001434047722200225515ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/site/resources/download_net.cgi000077500000000000000000000002351434047722200257150ustar00rootroot00000000000000#!/bin/sh # Just call the standard mirrors.cgi script. It will use download.html # as the input template. exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $*commons-net-rel-commons-net-3.9.0/src/site/resources/images/000077500000000000000000000000001434047722200240165ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/site/resources/images/net-logo-white.png000066400000000000000000000246611434047722200273770ustar00rootroot00000000000000PNG  IHDRQƥvsRGBbKGD pHYs  tIME 2sU!iTXtCommentCreated with The GIMPm IDATx}{pM^3N@UE @b"Jc-Z:K*#JEfPi JbiDM B Kn({Ϝ /++SgϞu<Ν:@k1cڶm *ÇgyFd@z:yF^bT9R}̙3̙3S1T_tԨRpд>|Ug6z,XS>jڴiFZXA駟ژ477W_=EEEcꫯb c$W^y3gθOӜmf@@mݺѮ.u9,M׿@ `U|>WܹsEZʕ+żV8pƐ'Ntmիžݻwc<)`$/,,T VVڪ/f-i'ӧmKѣg} (FF#G$X6Gî]0~x뽑#G޳96ϰap1.HHH@BB.ԧO^ƂZJlNsŊ+0a-[fG5w\[J\{FMobb"*++q}9\jnv#F8-[ &{cΝ;رc#uСhiiinn FyX||XFfʕϷ*]v8ݾ9s_ >Nկ~*9|tOD(~7tz)sAUU}Q :Tl lAlG}d<33S%'q5]kkLw]vꫯv0 yӚ| D믩 6i1)8LjKk꼜y9Bk:<%;;Qݻq7"!!F Z1X>іK߾}q=g'Nİaðh"(9mpoI},MXy^/Q{P`B;/55'Ow1\y啎|n@@jbb:/o﷾3i>WcA*S.H(j`K󡱱QdPA-ySSSe8q 7Vk(ks|ӧĉؼy#ݻqM7]w݅ HR6|I]y:?]j2Z3evZWҘ|WnM$ h4joMy%@KIkLR C:] LY-i;uH Y9|bݺub_}Ul޼?0i$&in U;4&.q;^u6o,ϵd"],74/5yn7WyjkW7Hcd~S nʃ, \n# BnpӧOիEXt) C2_ ---F)M%@'&&5আ )))m1K-q-\x&9LVܘ{ 5RҚKZyTՏ7&AC׶-<5i&̜9XN[[&Oj%iWLlܸ14iKO~zX͛E)%uC`v`R&ҋQ%:b2 H)g\qS(]ԈI5n5.-yYZm|ɎƑH&MBee%&MdepqQ"R(..C$KҘ5(ѫW/ ~y erVP'MC qhjf)y1 ܻ&%݀VT}|j6^5IqIr)Syyy"8EeCc2oUiaΝ9,yN;`ԩ1iM L\`5ћoY4L).L4Ӽ[ EKkeiס1:[Zm}wiiiqHM(J),X6lp-x0k,G[n-Ay1srrӧODZcBQwɓK/9IujllDcck$7G5oY~n@Rm.^g,Nc5c<*ɋ1F_qHNNv[o3[q9hMI58DO>D,?yyy(,,t*8YjE8u!ӦMג-ZǏ6|rɸ0|p811Ç5\iZ{Ezz`zr&idӠJ+@0 NDO?Oz 1Ɲ_u5ѓi۶m"H}WTTdtLI&ɣn[.6m5hjFQ^%%%8~8/_..$$$^pQ^^nEyy Rpt<#F`2jfyK;%3m(nm0a `{UUU"05QZsω<b+**1΄[\y啮K03L6 w}7/_>555زe /^[nO?4^@zzQedd`ɒ%ߞ|Iq]k֬ӭ{7px9<aU}bOɔ{;sLIvZis 0.]kخXiJ!UT6|GvxwZ*lb/۱qF̟?O>$̙[q/=o6}~,XTJwߵ@zz:***.2}q0kjj<5lyy)m^-Ϥkjjylʀ;-[x d,XF'OFYYܷo,Y &ɓHKKÒ%Kp뭷AIACeeO/)GCCD/ZGp 5kcpsB^zC꧟~Sb޽cҤIBRRuKC xW~zOM1d<ø,iB7IB!TWW[nŁ뇁b6/C܌?73vX3}_hjjQ[[k̛cb۷/.R455 ~`РA8p  `I7}8f׼F^^KijjªU,0 {w+4n ߳go_~=z4 ` :t}jkk 47pFmgEGGRRR`855tBuuզ;'NtĢVzBRR.C Hg};w޳Srr2 <~޽{ R}`{ACCp7bСle`0ٳgIUAA ol rW^oJ)TUU3RYY):~am>qmUrѶ4o~~>.rG74#;;^{m,ׂk}CA)6m~p4"?C贄Ѻl<ޖJ+W硝H;/11&-d б4 F-z܊dAPcMyUN/\P ܦW^b=EZ v|5۹-t[()`\>B;4(|~JéJ,9frZC2[]$8K5dжrfR2H9%.jL=2gqjO.(?r`rꗴ$ zךt½XhGZ'ŨJ)܊[im&R t?i%ӎ;2$ytly[%˜si۵Iha4HrP`߸c,7yXg/tU~>L)^z[T *CW|էab rcEOӏޤEZ"wJΆu[SD"y|CmW^B+%e6 8]O ޯ:L"M,1y/R5 0%&1nc/!ƻGNՐ6)),KB:M3]2=kݒs|So+}#1{* z262z*cB2//G5nf-mjL/IX4p,Weh/)?KѣXx1233TTTXdee9991}>G"++KԢ=~hii|si_\˧P!EQBO'/=tNPTII(WXmIDATD"*׫,7.m=@@mݺU(X;ʜ>} |:O,OggTS:uJ}W̙3ٳK շ'H$Ѩxґ#GԨQL^RRz=QVVΞ=+>ΝS֭ya @@=3&NԹsԹsleY/RuvvvڪNTUU|rq1*;_|>3.hpꔙ&;+ ÎkҥhiiAFF &ħ^a[Y@uF_BSBdi-irȐ!Tcj햞 6^bƍ[Gp`i4-1/S<]Z8.A>c׮];w-ˁ"ƁO$`zJt9ט`cɒ%e+@@>[ I?YR?āO$8Mh܀I$D"}ڥ)}`x՜\J1q`E%|6`ݻ@xc5?״|C'z*SSǁO^~q&բɓ'qQ7m7i8z&զǗK+up/-ӯ_?ﯾ#йݻś%󗯱q`E}% GQ 8{yy9;XnEkhh-[65m3.*rKR yyyLb;hҥKq7O7qlq`EMM8][[JKK蘳jMC)Af:;[äw7cNꪫp 7`۶m;1c1cƠ|8pmۆ6 IAUSSSZs9s`РA())Z80I۷o>;vHn6򶷷[ँf¼yP[[k{8p{  եO~lٲz-FٳcW<'8>t!{sNlڴh .[ٿh2>#3f' fB^^q!55z555hkkܹsŋ$}|W<]Pi]]/^XԧKsp8u֡9s@ZqF455Y_~3f "޿ChimmŢEP]] iӦ!99m$z与/xɗC48R;ZJ +.[U)Z 6׊h ^r%礤$Vv|OU2%F =7;fخvnq` @C}_~P,WUN$i~*f<] 7E"Tkղ^wwzXOT!5c%1tǥu~*CMWiMwz$Kw=.xXCFڄy.M.tn];Z_kGk80'Z:bL[ t 7R}P[[Yf.xpIA,Ҡ'](ŷj}7jI$|r|>qXr%~?vލ޽{cʕxᇱvZa\taR~@+ɫˆgߓw+i0=D:+k~ cbǎرc*477Mxx?5וzrռ/wPpJN& Lދ۷oGUUU\c1Ԛ[8ɻjxNR7&}2240Grr2~\zKb]YڥwD]8,pWz--p`~;Hbe5@aw̘LLkݽ4k^)PL.x` > bW|"q-NSP@O7wM!x&pIMh":GwN7ꔜJPX>xNS8bS^- u\HtrޛA IENDB`commons-net-rel-commons-net-3.9.0/src/site/site.xml000066400000000000000000000035571434047722200222370ustar00rootroot00000000000000 Apache Commons Net /images/net-logo-white.png /index.html Apache Commons Net&trade; logo commons-net-rel-commons-net-3.9.0/src/site/xdoc/000077500000000000000000000000001434047722200214745ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/site/xdoc/code-standards.xml000066400000000000000000000112321434047722200251100ustar00rootroot00000000000000 Coding Standards Jon S. Stevens Jason van Zyl

This document describes a list of coding conventions that are required for code submissions to the project. By default, the coding conventions for most Open Source Projects should follow the existing coding conventions in the code that you are working on. For example, if the bracket is on the same line as the if statement, then you should write all your code to have that convention.

If you commit code that does not follow these conventions, you are responsible for also fixing your own code.

Below is a list of coding conventions that are specific to Apache Commons Net everything else not specificially mentioned here should follow the official Sun Java Coding Conventions.

1. Brackets should begin and end on a new line and should exist even for one line statements. Examples:

2. Though it's considered okay to include spaces inside parens, the preference is to not include them. Both of the following are okay:

3. 4 space indent. NO tabs. Period. We understand that many developers like to use tabs, but the fact of the matter is that in a distributed development environment where diffs are sent to the mailing lists by both developers and the version control system (which sends commit log messages), the use tabs makes it impossible to preserve legibility.

In Emacs-speak, this translates to the following command:

4. Native linefeeds (svn:eol-style native) for all .java source code and text files. Platform specific files should have the platform specific linefeeds.

5. Javadoc MUST exist on all public and protected methods. Javadoc on private and default access methods and members is preferred and encouraged. If your code modifications use an existing class/method/variable which lacks Javadoc, it is required that you add it. This will improve the project as a whole.

6. The Apache License header MUST be placed at the top of each and every file.

9. Import statements must be fully qualified for clarity.

And not


X/Emacs users might appreciate this in their .emacs file.

Thanks for your cooperation.

commons-net-rel-commons-net-3.9.0/src/site/xdoc/download_net.xml000066400000000000000000000177411434047722200247050ustar00rootroot00000000000000 Download Apache Commons Net Apache Commons Documentation Team

We recommend you use a mirror to download our release builds, but you must verify the integrity of the downloaded files using signatures downloaded from our main distribution directories. Recent releases (48 hours) may not yet be available from all the mirrors.

You are currently using [preferred]. If you encounter a problem with this mirror, please select another mirror. If all mirrors are failing, there are backup mirrors (at the end of the mirrors list) that should be available.

[if-any logo][end]

Other mirrors:

It is essential that you verify the integrity of downloaded files, preferably using the PGP signature (*.asc files); failing that using the SHA512 hash (*.sha512 checksum files).

The KEYS file contains the public PGP keys used by Apache Commons developers to sign releases.

commons-net-3.9.0-bin.tar.gz sha512 pgp
commons-net-3.9.0-bin.zip sha512 pgp
commons-net-3.9.0-src.tar.gz sha512 pgp
commons-net-3.9.0-src.zip sha512 pgp

Older releases can be obtained from the archives.

commons-net-rel-commons-net-3.9.0/src/site/xdoc/faq.xml000066400000000000000000000031761434047722200227740ustar00rootroot00000000000000 FAQ - Frequently Asked Questions Apache Commons User Mailing List

This document attempts to answer some of the more frequently asked questions regarding various aspects of Commons/Net. These questions are typically asked over and over again on the mailing lists, as a courtesy to the developers, we ask that you read this document before posting to the mailing lists.

The FAQ is hosted on the Commons Wiki; please see: CommonsNet/FrequentlyAskedQuestions

commons-net-rel-commons-net-3.9.0/src/site/xdoc/index.xml000066400000000000000000000251271434047722200233340ustar00rootroot00000000000000 Overview Apache Commons Documentation Team

Apache Commons Net™ library implements the client side of many basic Internet protocols. The purpose of the library is to provide fundamental protocol access, not higher-level abstractions. Therefore, some of the design violates object-oriented design principles. Our philosophy is to make the global functionality of a protocol accessible (e.g., TFTP send file and receive file) when possible, but also provide access to the fundamental protocols where applicable so that the programmer may construct his own custom implementations (e.g, the TFTP packet classes and the TFTP packet send and receive methods are exposed).

Supported protocols include:

  • FTP/FTPS
  • FTP over HTTP (experimental)
  • NNTP
  • SMTP(S)
  • POP3(S)
  • IMAP(S)
  • Telnet
  • TFTP
  • Finger
  • Whois
  • rexec/rcmd/rlogin
  • Time (rdate) and Daytime
  • Echo
  • Discard
  • NTP/SNTP

Apache Jakarta Commons Net started as a commercial Java library called NetComponents, originally developed by ORO, Inc. in the early days of Java. After its 1.3.8 release in 1998, the source code was donated to the Apache Software Foundation and made available under the Apache License. Since then, many programmers have contributed to the continued development of Jakarta Commons Net. The current version numbering scheme bears no relation to the old. In other words, Jakarta Commons Net 1.0 succeeded and supplanted NetComponents 1.3.8. Apache Jakarta Commons is now an independent project and is called Apache Commons.

Commons NET includes several working sample applications that you can use. Source files are included in the source (and binary) archives, and a compiled jar is provided.

To use one of the sample applications, ensure that the example and main jars are both in the same directory. Then run the class as per the following example:
java -jar [path/]commons-net-examples-3.8.0.jar FTPClientExample [parameters]
This uses the helper application which supports shorthand class names.
Alternatively, ensure that the example and main jars are on the classpath. Then invoke the class directly, for example:
java -cp commons-net-examples-3.8.0.jar;commons-net-3.8.0.jar examples/ftp/FTPClientExample [parameters]
  • FTPClientExample demonstrates file download and upload, LIST, MLST etc over FTP(S) and FTP over HTTP
  • ServerToServerFTP This program arranges a server to server file transfer that transfers a file from host1 to host2.
  • TFTPExample This is an example of a simple Java tftp client
  • IMAPMail This is an example program demonstrating how to use the IMAP[S]Client class.
  • POP3Mail This is an example program demonstrating how to use the POP3[S]Client class.
  • SMTPMail This is an example program demonstrating how to use the SMTP[S]Client class.
  • ArticleReader Simple class showing one way to read an article header and body.
  • ExtendedNNTPOps Simple class showing some of the extended commands (AUTH, XOVER, LIST ACTIVE)
  • ListNewsgroups This is a simple example using the NNTP package to approximate the Unix newsgroups command. It connects to the specified news server and issues fetches the list of newsgroups stored by the server. On servers that store a lot of newsgroups, this command can take a very long time (listing upwards of 30,000 groups).
  • MessageThreading Sample program demonstrating the use of article iteration and threading.
  • PostMessage This is an example program using the NNTP package to post an article to the specified newsgroup(s). It prompts you for header information and a filename to post.
  • NTPClient This is an example program demonstrating how to use the NTPUDPClient class. This program sends a Datagram client request packet to a Network time Protocol (NTP) service port on a specified server, retrieves the time, and prints it to standard output along with the fields from the NTP message header (e.g. stratum level, reference id, poll interval, root delay, mode, ...)
  • TimeClient This is an example program demonstrating how to use the TimeTCPClient and TimeUDPClient classes. This program connects to the default time service port of a specified server, retrieves the time, and prints it to standard output.
  • TelnetClientExample This is a simple example of use of TelnetClient.
  • WeatherTelnet This is an example of a trivial use of the TelnetClient class. It connects to the weather server at the University of Michigan, um-weather.sprl.umich.edu port 3000, and allows the user to interact with the server via standard input.
  • chargen This is a simple example of use of chargen.
  • daytime This is a simple example of use of daytime.
  • echo This is a simple example of use of echo.
  • finger This is a simple example of use of finger.
  • fwhois This is a simple example of use of fwhois.
  • rdate This is a simple example of use of rdate.
  • rexec This is a simple example of use of rexec.
  • rlogin This is a simple example of use of rlogin.
  • rshell This is a simple example of use of rshell.

For more info, see the Javadoc, or look at some of the following articles:

commons-net-rel-commons-net-3.9.0/src/site/xdoc/issue-tracking.xml000066400000000000000000000133111434047722200251450ustar00rootroot00000000000000 Apache Commons Net Issue tracking Apache Commons Documentation Team

Apache Commons Net uses ASF JIRA for tracking issues. See the Apache Commons Net JIRA project page.

To use JIRA you may need to create an account (if you have previously created/updated Commons issues using Bugzilla an account will have been automatically created and you can use the Forgot Password page to get a new password).

If you would like to report a bug, or raise an enhancement request with Apache Commons Net please do the following:

  1. Search existing open bugs. If you find your issue listed then please add a comment with your details.
  2. Search the mailing list archive(s). You may find your issue or idea has already been discussed.
  3. Decide if your issue is a bug or an enhancement.
  4. Submit either a bug report or enhancement request.

Please also remember these points:

  • the more information you provide, the better we can help you
  • test cases are vital, particularly for any proposed enhancements
  • the developers of Apache Commons Net are all unpaid volunteers

For more information on creating patches see the Apache Contributors Guide.

You may also find these links useful:

commons-net-rel-commons-net-3.9.0/src/site/xdoc/mail-lists.xml000066400000000000000000000237001434047722200242760ustar00rootroot00000000000000 Apache Commons Net Mailing Lists Apache Commons Documentation Team

Apache Commons Net shares mailing lists with all the other Commons Components. To make it easier for people to only read messages related to components they are interested in, the convention in Commons is to prefix the subject line of messages with the component's name, for example:

  • [net] Problem with the ...

Questions related to the usage of Apache Commons Net should be posted to the User List.
The Developer List is for questions and discussion related to the development of Apache Commons Net.
Please do not cross-post; developers are also subscribed to the user list.
You must be subscribed to post to the mailing lists. Follow the Subscribe links below to subscribe.

Note: please don't send patches or attachments to any of the mailing lists. Patches are best handled via the Issue Tracking system. Otherwise, please upload the file to a public server and include the URL in the mail.

Please prefix the subject line of any messages for Apache Commons Net with [net] - thanks!

Name Subscribe Unsubscribe Post Archive Other Archives
Commons User List

Questions on using Apache Commons Net.

Subscribe Unsubscribe Post mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com
news.gmane.org
Commons Developer List

Discussion of development of Apache Commons Net.

Subscribe Unsubscribe Post mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com
news.gmane.org
Commons Issues List

Only for e-mails automatically generated by the issue tracking system.

Subscribe Unsubscribe read only mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com
Commons Commits List

Only for e-mails automatically generated by the source control system.

Subscribe Unsubscribe read only mail-archives.apache.org
lists.apache.org
markmail.org
www.mail-archive.com

Other mailing lists which you may find useful include:

Name Subscribe Unsubscribe Post Archive Other Archives
Apache Announce List

General announcements of Apache project releases.

Subscribe Unsubscribe read only mail-archives.apache.org
lists.apache.org
markmail.org
old.nabble.com
www.mail-archive.com
news.gmane.org
commons-net-rel-commons-net-3.9.0/src/site/xdoc/migration.xml000066400000000000000000000075511434047722200242170ustar00rootroot00000000000000 Migration How-to Jeffrey D. Brekke

This how-to lists the migration steps for moving between versions of Apache Commons Net.

Version 3.5 is binary compatible with previous 3.x versions and 2.0. There should be no changes required to existing binary code.

Version 3.5 is source compatible with 3.4. However, version 3.4 is not source compatible with 3.3.

The interface NtpV3Packet has been updated to add 3 new methods. Adding methods to an interface does not affect binary compatibility The clirr report shows which methods have been added. (note that the report does not distinguish between source and binary incompatibility) Code that uses the interface will need to be updated and recompiled. However code that uses the implementation class NtpV3Impl will continue to work as before.

Version 3.0 is binary compatible with version 2.0. There should be no changes required to existing binary code.

However, version 3.0 is not source compatible with 2.0.

Several obsolete/unused constants have been removed.
(Such changes do not affect binary code, because compilers are required to localise constants).
The clirr report shows which constants have been removed. If any source code happens to be using one of these constants, then the source will have to be updated.

Also, some throws clauses have been removed from methods which did not actually throw them.
Throws clauses are not part of method signatures, so do not affect binary compatibility.
The following public methods no longer throw IOException:

  • TelnetClient#addOptionHandler(TelnetOptionHandler)
  • TelnetClient#deleteOptionHandler(int)
Source code using these methods will need to be updated.

Version 2.0 requires a JDK 5.0+ runtime. It has also been tested on JDK 6.0. There should be no changes required to existing client code.

This version is a drop in replacement for NetComponents. Only package names have changed.

  1. Change all occurrences of com.oroinc.net.* to org.apache.commons.net.*
  2. Change all occurrences of com.oroinc.io.* to org.apache.commons.net.io.*
  3. Change all occurrences of com.oroinc.util.* to org.apache.commons.net.util.*
commons-net-rel-commons-net-3.9.0/src/site/xdoc/tasks.xml000066400000000000000000000040071434047722200233440ustar00rootroot00000000000000 Tasks Jeffrey D. Brekke Rory Winston

  • Add more unit tests, and implement mock servers for testing
  • Convert code to specified coding standards. Checkstyle report provided.
  • Clean out any classes that don't belong in this project. Probably classes from org.apache.commons.net.util and org.apache.commons.net.io could be moved to their corresponding commons projects.
  • Parse the client/server interactions without creating so many strings. Many operations are slow because of this.
  • Add ESMTP and more extended NNTP commands.
  • Make NNTPClient.listNewsgroups() and NNTPClient.listNewNews() more efficient. Don't preparse into lots of little objects.
  • FTP proxy support

commons-net-rel-commons-net-3.9.0/src/test/000077500000000000000000000000001434047722200205525ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/000077500000000000000000000000001434047722200214735ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/000077500000000000000000000000001434047722200222625ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/000077500000000000000000000000001434047722200235035ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/000077500000000000000000000000001434047722200251565ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/000077500000000000000000000000001434047722200257445ustar00rootroot00000000000000SocketClientFunctionalTest.java000066400000000000000000000042551434047722200340100ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.net.InetSocketAddress; import java.net.Proxy; import org.apache.commons.net.ftp.FTPClient; import junit.framework.TestCase; /** * A simple functional test class for SocketClients. * * Requires a Java-compatible SOCK proxy server on 127.0.0.1:9050 and access to ftp.gnu.org. */ public class SocketClientFunctionalTest extends TestCase { private static final String PROXY_HOST = "127.0.0.1"; private static final int PROXY_PORT = 9050; private static final String DEST_HOST = "ftp.gnu.org"; private static final int DEST_PORT = 21; // any subclass will do, but it should be able to connect to the destination host SocketClient sc = new FTPClient(); /** * The constructor for this test case. * * @param name passed to TestCase */ public SocketClientFunctionalTest(final String name) { super(name); } /** * A simple test to verify that the Proxy settings work. * * @throws Exception in case of connection errors */ public void testProxySettings() throws Exception { // NOTE: HTTP Proxies seem to be invalid for raw sockets final Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(PROXY_HOST, PROXY_PORT)); sc.setProxy(proxy); sc.connect(DEST_HOST, DEST_PORT); assertTrue(sc.isConnected()); sc.disconnect(); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/SocketClientTest.java000066400000000000000000000032151434047722200320370ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import java.net.InetSocketAddress; import java.net.Proxy; import org.apache.commons.net.ftp.FTPClient; import junit.framework.TestCase; /** * A simple test class for SocketClient settings. * * @since 3.2 */ public class SocketClientTest extends TestCase { private static final String PROXY_HOST = "127.0.0.1"; private static final int PROXY_PORT = 1080; /** * A simple test to verify that the Proxy is being set. */ public void testProxySettings() { final SocketClient socketClient = new FTPClient(); assertNull(socketClient.getProxy()); final Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(PROXY_HOST, PROXY_PORT)); socketClient.setProxy(proxy); assertEquals(proxy, socketClient.getProxy()); assertFalse(socketClient.isConnected()); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/SubnetUtilsTest.java000066400000000000000000000443021434047722200317330ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import org.apache.commons.net.util.SubnetUtils; import org.apache.commons.net.util.SubnetUtils.SubnetInfo; import junit.framework.TestCase; @SuppressWarnings("deprecation") // deliberate use of deprecated methods public class SubnetUtilsTest extends TestCase { public void testAddresses() { final SubnetUtils utils = new SubnetUtils("192.168.0.1/29"); final SubnetInfo info = utils.getInfo(); assertTrue(info.isInRange("192.168.0.1")); assertTrue(info.isInRange("192.168.0.2")); assertTrue(info.isInRange("192.168.0.3")); assertTrue(info.isInRange("192.168.0.4")); assertTrue(info.isInRange("192.168.0.5")); assertTrue(info.isInRange("192.168.0.6")); // We don't count the broadcast address as usable assertFalse(info.isInRange("192.168.0.7")); assertFalse(info.isInRange("192.168.0.8")); assertFalse(info.isInRange("10.10.2.1")); assertFalse(info.isInRange("192.168.1.1")); assertFalse(info.isInRange("192.168.0.255")); // assertEquals(-1062731775, info.asInteger("192.168.0.1")); assertThrows(IllegalArgumentException.class, () -> info.asInteger("bad")); // assertArrayEquals(new String[] { "192.168.0.1", "192.168.0.2", "192.168.0.3", "192.168.0.4", "192.168.0.5", "192.168.0.6" }, info.getAllAddresses()); } public void testAddressIllegalArgument() { assertThrows(IllegalArgumentException.class, () -> new SubnetUtils("bad")); } /** * Test using the inclusiveHostCount flag, which includes the network and broadcast addresses in host counts */ public void testCidrAddresses() { SubnetUtils utils = new SubnetUtils("192.168.0.1/8"); utils.setInclusiveHostCount(true); SubnetInfo info = utils.getInfo(); assertEquals("255.0.0.0", info.getNetmask()); assertEquals(16777216, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/0"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("0.0.0.0", info.getNetmask()); assertEquals(4294967296L, info.getAddressCountLong()); utils = new SubnetUtils("192.168.0.1/1"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("128.0.0.0", info.getNetmask()); assertEquals(2147483648L, info.getAddressCountLong()); utils = new SubnetUtils("192.168.0.1/9"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.128.0.0", info.getNetmask()); assertEquals(8388608, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/10"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.192.0.0", info.getNetmask()); assertEquals(4194304, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/11"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.224.0.0", info.getNetmask()); assertEquals(2097152, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/12"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.240.0.0", info.getNetmask()); assertEquals(1048576, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/13"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.248.0.0", info.getNetmask()); assertEquals(524288, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/14"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.252.0.0", info.getNetmask()); assertEquals(262144, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/15"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.254.0.0", info.getNetmask()); assertEquals(131072, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/16"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.0.0", info.getNetmask()); assertEquals(65536, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/17"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.128.0", info.getNetmask()); assertEquals(32768, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/18"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.192.0", info.getNetmask()); assertEquals(16384, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/19"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.224.0", info.getNetmask()); assertEquals(8192, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/20"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.240.0", info.getNetmask()); assertEquals(4096, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/21"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.248.0", info.getNetmask()); assertEquals(2048, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/22"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.252.0", info.getNetmask()); assertEquals(1024, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/23"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.254.0", info.getNetmask()); assertEquals(512, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/24"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.0", info.getNetmask()); assertEquals(256, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/25"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.128", info.getNetmask()); assertEquals(128, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/26"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.192", info.getNetmask()); assertEquals(64, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/27"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.224", info.getNetmask()); assertEquals(32, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/28"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.240", info.getNetmask()); assertEquals(16, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/29"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.248", info.getNetmask()); assertEquals(8, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/30"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.252", info.getNetmask()); assertEquals(4, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/31"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.254", info.getNetmask()); assertEquals(2, info.getAddressCount()); utils = new SubnetUtils("192.168.0.1/32"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("255.255.255.255", info.getNetmask()); assertEquals(1, info.getAddressCount()); } public void testInvalidMasks() { try { new SubnetUtils("192.168.0.1/33"); fail("Should have thrown IllegalArgumentException"); } catch (final IllegalArgumentException expected) { // Ignored } } public void testNET428_31() { final SubnetUtils subnetUtils = new SubnetUtils("1.2.3.4/31"); assertEquals(0, subnetUtils.getInfo().getAddressCount()); final String[] address = subnetUtils.getInfo().getAllAddresses(); assertNotNull(address); assertEquals(0, address.length); } public void testNET428_32() { final SubnetUtils subnetUtils = new SubnetUtils("1.2.3.4/32"); assertEquals(0, subnetUtils.getInfo().getAddressCount()); final String[] address = subnetUtils.getInfo().getAllAddresses(); assertNotNull(address); assertEquals(0, address.length); } public void testNET520() { final SubnetUtils utils = new SubnetUtils("0.0.0.0/0"); utils.setInclusiveHostCount(true); final SubnetInfo info = utils.getInfo(); assertEquals("0.0.0.0", info.getNetworkAddress()); assertEquals("255.255.255.255", info.getBroadcastAddress()); assertTrue(info.isInRange("127.0.0.0")); utils.setInclusiveHostCount(false); assertTrue(info.isInRange("127.0.0.0")); } public void testNET521() { SubnetUtils utils; SubnetInfo info; utils = new SubnetUtils("0.0.0.0/0"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("0.0.0.0", info.getNetmask()); assertEquals(4294967296L, info.getAddressCountLong()); try { info.getAddressCount(); fail("Expected RuntimeException"); } catch (final RuntimeException expected) { // ignored } utils = new SubnetUtils("128.0.0.0/1"); utils.setInclusiveHostCount(true); info = utils.getInfo(); assertEquals("128.0.0.0", info.getNetmask()); assertEquals(2147483648L, info.getAddressCountLong()); try { info.getAddressCount(); fail("Expected RuntimeException"); } catch (final RuntimeException expected) { // ignored } // if we exclude the broadcast and network addresses, the count is less than Integer.MAX_VALUE utils.setInclusiveHostCount(false); info = utils.getInfo(); assertEquals(2147483646, info.getAddressCount()); } public void testNET624() { new SubnetUtils("0.0.0.0/0"); new SubnetUtils("0.0.0.0", "0.0.0.0"); new SubnetUtils("0.0.0.0", "128.0.0.0"); try { new SubnetUtils("0.0.0.0", "64.0.0.0"); fail("Should have thrown IllegalArgumentException"); } catch (final IllegalArgumentException expected) { // Ignored } try { new SubnetUtils("0.0.0.0", "0.0.0.1"); fail("Should have thrown IllegalArgumentException"); } catch (final IllegalArgumentException expected) { // Ignored } } public void testNET641() { assertFalse(new SubnetUtils("192.168.1.0/00").getInfo().isInRange("0.0.0.0")); assertFalse(new SubnetUtils("192.168.1.0/30").getInfo().isInRange("0.0.0.0")); assertFalse(new SubnetUtils("192.168.1.0/31").getInfo().isInRange("0.0.0.0")); assertFalse(new SubnetUtils("192.168.1.0/32").getInfo().isInRange("0.0.0.0")); } public void testNET675() { final SubnetUtils utils = new SubnetUtils("192.168.0.15/32"); utils.setInclusiveHostCount(true); final SubnetInfo info = utils.getInfo(); assertTrue(info.isInRange("192.168.0.15")); } public void testNET679() { final SubnetUtils utils = new SubnetUtils("10.213.160.0/16"); utils.setInclusiveHostCount(true); final SubnetInfo info = utils.getInfo(); assertTrue(info.isInRange("10.213.0.0")); assertTrue(info.isInRange("10.213.255.255")); } public void testNext() { final SubnetUtils utils = new SubnetUtils("192.168.0.1/29"); assertEquals("192.168.0.2", utils.getNext().getInfo().getAddress()); } public void testParseSimpleNetmask() { final String address = "192.168.0.1"; final String masks[] = { "255.0.0.0", "255.255.0.0", "255.255.255.0", "255.255.255.248" }; final String bcastAddresses[] = { "192.255.255.255", "192.168.255.255", "192.168.0.255", "192.168.0.7" }; final String lowAddresses[] = { "192.0.0.1", "192.168.0.1", "192.168.0.1", "192.168.0.1" }; final String highAddresses[] = { "192.255.255.254", "192.168.255.254", "192.168.0.254", "192.168.0.6" }; final String nextAddresses[] = { "192.168.0.2", "192.168.0.2", "192.168.0.2", "192.168.0.2" }; final String previousAddresses[] = { "192.168.0.0", "192.168.0.0", "192.168.0.0", "192.168.0.0" }; final String networkAddresses[] = { "192.0.0.0", "192.168.0.0", "192.168.0.0", "192.168.0.0" }; final String cidrSignatures[] = { "192.168.0.1/8", "192.168.0.1/16", "192.168.0.1/24", "192.168.0.1/29" }; final int usableAddresses[] = { 16777214, 65534, 254, 6 }; for (int i = 0; i < masks.length; ++i) { final SubnetUtils utils = new SubnetUtils(address, masks[i]); final SubnetInfo info = utils.getInfo(); assertEquals(address, info.getAddress()); assertEquals(bcastAddresses[i], info.getBroadcastAddress()); assertEquals(cidrSignatures[i], info.getCidrSignature()); assertEquals(lowAddresses[i], info.getLowAddress()); assertEquals(highAddresses[i], info.getHighAddress()); assertEquals(nextAddresses[i], info.getNextAddress()); assertEquals(previousAddresses[i], info.getPreviousAddress()); assertEquals(networkAddresses[i], info.getNetworkAddress()); assertEquals(usableAddresses[i], info.getAddressCount()); } } public void testParseSimpleNetmaskExclusive() { final String address = "192.168.15.7"; final String masks[] = { "255.255.255.252", "255.255.255.254", "255.255.255.255" }; final String bcast[] = { "192.168.15.7", "192.168.15.7", "192.168.15.7" }; final String netwk[] = { "192.168.15.4", "192.168.15.6", "192.168.15.7" }; final String lowAd[] = { "192.168.15.5", "0.0.0.0", "0.0.0.0" }; final String highA[] = { "192.168.15.6", "0.0.0.0", "0.0.0.0" }; final String cidrS[] = { "192.168.15.7/30", "192.168.15.7/31", "192.168.15.7/32" }; final int usableAd[] = { 2, 0, 0 }; // low and high addresses don't exist for (int i = 0; i < masks.length; ++i) { final SubnetUtils utils = new SubnetUtils(address, masks[i]); utils.setInclusiveHostCount(false); final SubnetInfo info = utils.getInfo(); assertEquals("ci " + masks[i], cidrS[i], info.getCidrSignature()); assertEquals("bc " + masks[i], bcast[i], info.getBroadcastAddress()); assertEquals("nw " + masks[i], netwk[i], info.getNetworkAddress()); assertEquals("ac " + masks[i], usableAd[i], info.getAddressCount()); assertEquals("lo " + masks[i], lowAd[i], info.getLowAddress()); assertEquals("hi " + masks[i], highA[i], info.getHighAddress()); } } public void testParseSimpleNetmaskInclusive() { final String address = "192.168.15.7"; final String masks[] = { "255.255.255.252", "255.255.255.254", "255.255.255.255" }; final String bcast[] = { "192.168.15.7", "192.168.15.7", "192.168.15.7" }; final String netwk[] = { "192.168.15.4", "192.168.15.6", "192.168.15.7" }; final String lowAd[] = { "192.168.15.4", "192.168.15.6", "192.168.15.7" }; final String highA[] = { "192.168.15.7", "192.168.15.7", "192.168.15.7" }; final String cidrS[] = { "192.168.15.7/30", "192.168.15.7/31", "192.168.15.7/32" }; final int usableAd[] = { 4, 2, 1 }; for (int i = 0; i < masks.length; ++i) { final SubnetUtils utils = new SubnetUtils(address, masks[i]); utils.setInclusiveHostCount(true); final SubnetInfo info = utils.getInfo(); assertEquals("ci " + masks[i], cidrS[i], info.getCidrSignature()); assertEquals("bc " + masks[i], bcast[i], info.getBroadcastAddress()); assertEquals("ac " + masks[i], usableAd[i], info.getAddressCount()); assertEquals("nw " + masks[i], netwk[i], info.getNetworkAddress()); assertEquals("lo " + masks[i], lowAd[i], info.getLowAddress()); assertEquals("hi " + masks[i], highA[i], info.getHighAddress()); } } public void testPrevious() { final SubnetUtils utils = new SubnetUtils("192.168.0.1/29"); assertEquals("192.168.0.0", utils.getPrevious().getInfo().getAddress()); } public void testToString() { final SubnetUtils utils = new SubnetUtils("192.168.0.1/29"); assertDoesNotThrow(() -> utils.toString()); final SubnetInfo info = utils.getInfo(); assertDoesNotThrow(() -> info.toString()); } public void testZeroAddressAndCidr() { final SubnetUtils snu = new SubnetUtils("0.0.0.0/0"); assertNotNull(snu); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/examples/000077500000000000000000000000001434047722200275625ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/examples/MainTest.java000066400000000000000000000123161434047722200321540ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.examples; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URLDecoder; import java.security.CodeSource; import java.util.Enumeration; import java.util.Properties; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.junit.Test; public class MainTest { private static boolean hasMainMethod(String name) { name = name.replace(".class", ""); try { final Class clazz = Class.forName(name, false, MainTest.class.getClassLoader()); clazz.getMethod("main", String[].class); return true; } catch (final ClassNotFoundException e) { System.out.println("Cannot find " + name); return false; } catch (final NoSuchMethodException e) { return false; } catch (final SecurityException e) { e.printStackTrace(); } return true; } private static void processFileName(String name, final Properties p) { name = name.replace(File.separatorChar, '.'); if (!name.endsWith(".class") || name.contains("$") // subclasses || name.endsWith("examples.Main.class") // the initial class, don't want to add that || !hasMainMethod(name)) { return; } name = name.replace(".class", ""); final int lastSep = name.lastIndexOf('.'); final String alias = name.substring(lastSep + 1); if (p.containsKey(alias)) { System.out.printf("Duplicate alias: %-25s %s %s %n", alias, name, p.getProperty(alias)); } else { p.setProperty(alias, name); } } private static void scanForClasses(final int rootLength, final File current, final Properties p) { final File[] files = current.listFiles(); if (files != null) { for (final File file : files) { if (file.isDirectory()) { scanForClasses(rootLength, file, p); } else { processFileName(file.getPath().substring(rootLength), p); } } } } @Test public void checkExamplesPropertiesIsComplete() throws Exception { final Properties cp = scanClasses(); final Properties fp = new Properties(); try (final InputStream inputStream = this.getClass().getResourceAsStream("examples.properties")) { fp.load(inputStream); } @SuppressWarnings("unchecked") // OK final Enumeration propertyNames = (Enumeration) cp.propertyNames(); while (propertyNames.hasMoreElements()) { final String c = propertyNames.nextElement(); final String fv = fp.getProperty(c); final String cv = cp.getProperty(c); if (fv == null) { System.out.printf("%-25s %s - missing from examples.properties%n", c, cv); } else if (!fv.equals(cv)) { System.out.printf("%-25s %s - expected value %s %n", c, fv, cv); } } } private Properties scanClasses() throws IOException { final CodeSource codeSource = Main.class.getProtectionDomain().getCodeSource(); // ensure special characters are decoded OK by uing the charset // Use canonical path to ensure consistency with Windows final String sourceFile = new File(URLDecoder.decode(codeSource.getLocation().getFile(), "UTF-8")).getCanonicalPath(); final Properties p = new Properties(); if (sourceFile.endsWith(".jar")) { try (final JarFile jf = new JarFile(sourceFile)) { final Enumeration e = jf.entries(); while (e.hasMoreElements()) { final JarEntry je = e.nextElement(); final String name = je.getName(); processFileName(name, p); } } } else { final File examples = new File(sourceFile, "org/apache/commons/net/examples"); // must match top level examples package name if (examples.exists()) { // need to add 1 to allow for path separator between root and file scanForClasses(sourceFile.length() + 1, examples, p); } else { fail("Could not find examples classes: " + examples.getCanonicalPath()); } } return p; } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/000077500000000000000000000000001434047722200265355ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/AbstractFtpsTest.java000066400000000000000000000222051434047722200326410ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.net.SocketException; import java.net.URL; import java.time.Duration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.net.PrintCommandListener; import org.apache.ftpserver.FtpServer; import org.apache.ftpserver.FtpServerFactory; import org.apache.ftpserver.ftplet.FtpException; import org.apache.ftpserver.ftplet.UserManager; import org.apache.ftpserver.listener.ListenerFactory; import org.apache.ftpserver.ssl.SslConfiguration; import org.apache.ftpserver.ssl.SslConfigurationFactory; import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory; import org.apache.ftpserver.usermanager.impl.BaseUser; import org.junit.Assert; /** * Tests {@link FTPSClient}. *

* To get our test certificate to work on Java 11, this test must be run with: *

* *
 * -Djdk.tls.client.protocols="TLSv1.1"
 * 
*

* This test does the above programmatically. *

*/ public abstract class AbstractFtpsTest { private static int SocketPort; private static FtpServer EmbeddedFtpServer; protected static final boolean IMPLICIT = false; protected static final long TEST_TIMEOUT = 10000; // individual test timeout private static final boolean TRACE_CALLS = Boolean.parseBoolean(System.getenv("TRACE_CALLS")); private static final boolean ADD_LISTENER = Boolean.parseBoolean(System.getenv("ADD_LISTENER")); private static final long startTime = System.nanoTime(); /** * Returns the test directory as a String. * @param defaultHome A default value. * * @return the test directory as a String */ protected static String getTestHomeDirectory(final String defaultHome) { return System.getProperty("test.basedir", defaultHome); } /** * Creates and starts an embedded Apache MINA FTP Server. * * @param implicit FTPS connection mode. * @param userPropertiesResource resource path to user properties file. * @param serverJksResourceResource resource path to server JKS file. * @param defaultHome default home folder * @throws FtpException Thrown when a the FTP classes cannot fulfill a request. */ protected synchronized static void setupServer(final boolean implicit, final String userPropertiesResource, final String serverJksResourceResource, final String defaultHome) throws FtpException { if (EmbeddedFtpServer != null) { return; } // Let the OS find use an ephemeral port by using 0. SocketPort = 0; final FtpServerFactory serverFactory = new FtpServerFactory(); final PropertiesUserManagerFactory propertiesUserManagerFactory = new PropertiesUserManagerFactory(); final URL userPropsResource = ClassLoader.getSystemClassLoader().getResource(userPropertiesResource); Assert.assertNotNull(userPropertiesResource, userPropsResource); propertiesUserManagerFactory.setUrl(userPropsResource); final UserManager userManager = propertiesUserManagerFactory.createUserManager(); final BaseUser user = (BaseUser) userManager.getUserByName("test"); // Pickup the home dir value at runtime even though we have it set in the userprop file // The user prop file requires the "homedirectory" to be set user.setHomeDirectory(getTestHomeDirectory(defaultHome)); serverFactory.setUserManager(userManager); final ListenerFactory factory = new ListenerFactory(); factory.setPort(SocketPort); // define SSL configuration final URL serverJksResource = ClassLoader.getSystemClassLoader().getResource(serverJksResourceResource); Assert.assertNotNull(serverJksResourceResource, serverJksResource); System.out.println("Loading " + serverJksResource); final SslConfigurationFactory sllConfigFactory = new SslConfigurationFactory(); final File keyStoreFile = FileUtils.toFile(serverJksResource); Assert.assertTrue(keyStoreFile.toString(), keyStoreFile.exists()); sllConfigFactory.setKeystoreFile(keyStoreFile); sllConfigFactory.setKeystorePassword("password"); // set the SSL configuration for the listener final SslConfiguration sslConfiguration = sllConfigFactory.createSslConfiguration(); final NoProtocolSslConfigurationProxy noProtocolSslConfigurationProxy = new NoProtocolSslConfigurationProxy(sslConfiguration); factory.setSslConfiguration(noProtocolSslConfigurationProxy); factory.setImplicitSsl(implicit); // replace the default listener serverFactory.addListener("default", factory.createListener()); // start the server EmbeddedFtpServer = serverFactory.createServer(); EmbeddedFtpServer.start(); SocketPort = ((org.apache.ftpserver.impl.DefaultFtpServer) EmbeddedFtpServer).getListener("default").getPort(); // System.out.printf("jdk.tls.disabledAlgorithms = %s%n", System.getProperty("jdk.tls.disabledAlgorithms")); trace("Server started"); } protected static void trace(final String msg) { if (TRACE_CALLS) { System.err.println(msg + " " + (System.nanoTime() - startTime)); } } private final boolean endpointCheckingEnabled; public AbstractFtpsTest(final boolean endpointCheckingEnabled, final String userPropertiesResource, final String serverJksResource) { this.endpointCheckingEnabled = endpointCheckingEnabled; } protected void assertClientCode(final FTPSClient client) { final int replyCode = client.getReplyCode(); assertTrue(FTPReply.isPositiveCompletion(replyCode)); } protected FTPSClient loginClient() throws SocketException, IOException { trace(">>loginClient"); final FTPSClient client = new FTPSClient(IMPLICIT); if (ADD_LISTENER) { client.addProtocolCommandListener(new PrintCommandListener(System.err)); } // client.setControlKeepAliveReplyTimeout(null); assertEquals(0, client.getControlKeepAliveReplyTimeoutDuration().getSeconds()); client.setControlKeepAliveReplyTimeout(Duration.ofSeconds(60)); assertEquals(60, client.getControlKeepAliveReplyTimeoutDuration().getSeconds()); // client.setControlKeepAliveTimeout(null); assertEquals(0, client.getControlKeepAliveTimeoutDuration().getSeconds()); client.setControlKeepAliveTimeout(Duration.ofSeconds(61)); assertEquals(61, client.getControlKeepAliveTimeoutDuration().getSeconds()); // client.setDataTimeout(null); assertEquals(0, client.getDataTimeout().getSeconds()); client.setDataTimeout(Duration.ofSeconds(62)); assertEquals(62, client.getDataTimeout().getSeconds()); // client.setEndpointCheckingEnabled(endpointCheckingEnabled); client.connect("localhost", SocketPort); // assertClientCode(client); assertEquals(SocketPort, client.getRemotePort()); // try { // HACK: Without this sleep, the user command sometimes does not reach the ftpserver // This only seems to affect GitHub builds, and only Java 11+ Thread.sleep(200); // 100 seems to be not always enough } catch (final InterruptedException ignore) { // ignore } assertTrue(client.login("test", "test")); assertClientCode(client); // client.setFileType(FTP.BINARY_FILE_TYPE); assertClientCode(client); // client.execPBSZ(0); assertClientCode(client); // client.execPROT("P"); assertClientCode(client); trace("< getSortedSet(final FTPFile[] files) { // create a TreeSet which will sort each element // as it is added. final TreeSet sorted = new TreeSet<>((o1, o2) -> o1.getTimestamp().getTime().compareTo(o2.getTimestamp().getTime())); for (final FTPFile file : files) { // The directory contains a few additional files at the beginning // which aren't in the series we want. The series we want consists // of files named sn.dddd. This adjusts the file list to get rid // of the uninteresting ones. if (file.getName().startsWith("sn")) { sorted.add(file); } } return sorted; } /** * @throws Exception */ @Override protected void setUp() throws Exception { super.setUp(); ftpClientConfig = new FTPClientConfig(FTPClientConfig.SYST_UNIX); ftpClientConfig.setServerTimeZoneId("GMT"); ftpClient.configure(ftpClientConfig); try { ftpClient.connect("tgftp.nws.noaa.gov"); ftpClient.login("anonymous", "testing@apache.org"); ftpClient.changeWorkingDirectory("SL.us008001/DF.an/DC.sflnd/DS.metar"); ftpClient.enterLocalPassiveMode(); } catch (final IOException e) { e.printStackTrace(); } } /** * @throws Exception */ @Override protected void tearDown() throws Exception { ftpClient.disconnect(); super.tearDown(); } public void testTimeZoneFunctionality() throws Exception { final java.util.Date nowDate = new java.util.Date(); final Instant nowInstant = nowDate.toInstant(); final FTPFile[] files = ftpClient.listFiles(); final TreeSet sortedSet = getSortedSet(files); // SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm z" ); FTPFile lastFile = null; FTPFile firstFile = null; for (final FTPFile thisFile : sortedSet) { if (firstFile == null) { firstFile = thisFile; } // System.out.println(sdf.format(thisFile.getTimestamp().getTime()) // + " " +thisFile.getName()); if (lastFile != null) { // verify that the list is sorted earliest to latest. assertTrue(lastFile.getTimestamp().before(thisFile.getTimestamp())); assertTrue(lastFile.getTimestampInstant().isBefore(thisFile.getTimestampInstant())); } lastFile = thisFile; } if (firstFile == null || lastFile == null) { fail("No files found"); } else { // test that notwithstanding any time zone differences, the newest file // is older than now. assertTrue(lastFile.getTimestamp().getTime().before(nowDate)); assertTrue(lastFile.getTimestampInstant().isBefore(nowInstant)); final Calendar firstCal = firstFile.getTimestamp(); final Instant firstInstant = firstFile.getTimestampInstant().plus(Duration.ofDays(2)); // test that the oldest is less than two days older than the newest // and, in particular, that no files have been considered "future" // by the parser and therefore been relegated to the same date a // year ago. firstCal.add(Calendar.DAY_OF_MONTH, 2); assertTrue(lastFile.getTimestamp().getTime() + " before " + firstCal.getTime(), lastFile.getTimestamp().before(firstCal)); assertTrue(lastFile.getTimestampInstant() + " before " + firstInstant, lastFile.getTimestampInstant().isBefore(firstInstant)); } } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/FTPClientConfigTest.java000066400000000000000000000151311434047722200331570ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import junit.framework.TestCase; public class FTPClientConfigTest extends TestCase { private static final String A = "A"; private static final String B = "B"; private static final String C = "C"; private static final String D = "D"; private static final String E = "E"; private static final String F = "F"; private static final String badDelim = "jan,feb,mar,apr,may,jun,jul,aug.sep,oct,nov,dec"; private static final String tooLong = "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|jan"; private static final String tooShort = "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov"; private static final String fakeLang = "abc|def|ghi|jkl|mno|pqr|stu|vwx|yza|bcd|efg|hij"; /* * Class under test for void FTPClientConfig(String) */ public void testFTPClientConfigString() { final FTPClientConfig config = new FTPClientConfig(FTPClientConfig.SYST_VMS); assertEquals(FTPClientConfig.SYST_VMS, config.getServerSystemKey()); assertNull(config.getDefaultDateFormatStr()); assertNull(config.getRecentDateFormatStr()); assertNull(config.getShortMonthNames()); assertNull(config.getServerTimeZoneId()); assertNull(config.getServerLanguageCode()); } /* * Class under test for void FTPClientConfig(String, String, String, String, String, String) */ public void testFTPClientConfigStringStringStringStringStringString() { final FTPClientConfig conf = new FTPClientConfig(A, B, C, D, E, F); assertEquals("A", conf.getServerSystemKey()); assertEquals("B", conf.getDefaultDateFormatStr()); assertEquals("C", conf.getRecentDateFormatStr()); assertEquals("E", conf.getShortMonthNames()); assertEquals("F", conf.getServerTimeZoneId()); assertEquals("D", conf.getServerLanguageCode()); } public void testGetDateFormatSymbols() { try { FTPClientConfig.getDateFormatSymbols(badDelim); fail("bad delimiter"); } catch (final IllegalArgumentException e) { // should have failed } try { FTPClientConfig.getDateFormatSymbols(tooLong); fail("more than 12 months"); } catch (final IllegalArgumentException e) { // should have failed } try { FTPClientConfig.getDateFormatSymbols(tooShort); fail("fewer than 12 months"); } catch (final IllegalArgumentException e) { // should have failed } DateFormatSymbols dfs2 = null; try { dfs2 = FTPClientConfig.getDateFormatSymbols(fakeLang); } catch (final Exception e) { fail("rejected valid short month string"); } final SimpleDateFormat sdf1 = new SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH); final SimpleDateFormat sdf2 = new SimpleDateFormat("MMM dd, yyyy", dfs2); Date d1 = null; Date d2 = null; try { d1 = sdf1.parse("dec 31, 2004"); } catch (final ParseException px) { fail("failed.to.parse.std"); } try { d2 = sdf2.parse("hij 31, 2004"); } catch (final ParseException px) { fail("failed.to.parse.weird"); } assertEquals("different.parser.same.date", d1, d2); try { sdf1.parse("hij 31, 2004"); fail("should.have.failed.to.parse.weird"); } catch (final ParseException px) { // expected } try { sdf2.parse("dec 31, 2004"); fail("should.have.failed.to.parse.standard"); } catch (final ParseException px) { // expected } } public void testGetServerLanguageCode() { } public void testLookupDateFormatSymbols() { DateFormatSymbols dfs1 = null; DateFormatSymbols dfs2 = null; DateFormatSymbols dfs3 = null; DateFormatSymbols dfs4 = null; try { dfs1 = FTPClientConfig.lookupDateFormatSymbols("fr"); } catch (final IllegalArgumentException e) { fail("french"); } try { dfs2 = FTPClientConfig.lookupDateFormatSymbols("sq"); } catch (final IllegalArgumentException e) { fail("albanian"); } try { dfs3 = FTPClientConfig.lookupDateFormatSymbols("ru"); } catch (final IllegalArgumentException e) { fail("unusupported.default.to.en"); } try { dfs4 = FTPClientConfig.lookupDateFormatSymbols(fakeLang); } catch (final IllegalArgumentException e) { fail("not.language.code.but.defaults"); } assertEquals(dfs3, dfs4); final SimpleDateFormat sdf1 = new SimpleDateFormat("d MMM yyyy", dfs1); final SimpleDateFormat sdf2 = new SimpleDateFormat("MMM dd, yyyy", dfs2); final SimpleDateFormat sdf3 = new SimpleDateFormat("MMM dd, yyyy", dfs3); Date d1 = null; Date d2 = null; Date d3 = null; try { d1 = sdf1.parse("31 d\u00e9c 2004"); } catch (final ParseException px) { fail("failed.to.parse.french"); } try { d2 = sdf2.parse("dhj 31, 2004"); } catch (final ParseException px) { fail("failed.to.parse.albanian"); } try { d3 = sdf3.parse("DEC 31, 2004"); } catch (final ParseException px) { fail("failed.to.parse.'russian'"); } assertEquals("different.parser.same.date", d1, d2); assertEquals("different.parser.same.date", d1, d3); } public void testSetShortMonthNames() { } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/FTPClientTest.java000066400000000000000000000243411434047722200320340ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.ftp; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetAddress; import org.apache.commons.net.ftp.parser.UnixFTPEntryParser; import junit.framework.TestCase; public class FTPClientTest extends TestCase { private static class LocalClient extends FTPClient { private String systemType; @Override public String getSystemType() throws IOException { return systemType; } public void setSystemType(final String type) { systemType = type; } } private static class PassiveNatWorkAroundLocalClient extends FTPClient { private final String passiveModeServerIP; public PassiveNatWorkAroundLocalClient(final String passiveModeServerIP) { this.passiveModeServerIP = passiveModeServerIP; } @Override public InetAddress getRemoteAddress() { try { return InetAddress.getByName(passiveModeServerIP); } catch (final Exception e) { throw new RuntimeException(e); } } } private static final String[] TESTS = { "257 /path/without/quotes", "/path/without/quotes", "257 \"/path/with/delimiting/quotes/without/commentary\"", "/path/with/delimiting/quotes/without/commentary", "257 \"/path/with/quotes\"\" /inside/but/without/commentary\"", "/path/with/quotes\" /inside/but/without/commentary", "257 \"/path/with/quotes\"\" /inside/string\" and with commentary", "/path/with/quotes\" /inside/string", "257 \"/path/with/quotes\"\" /inside/string\" and with commentary that also \"contains quotes\"", "/path/with/quotes\" /inside/string", "257 \"/path/without/trailing/quote", // invalid syntax, return all after reply code prefix "\"/path/without/trailing/quote", "257 root is current directory.", // NET-442 "root is current directory.", "257 \"/\"", // NET-502 "/", }; public FTPClientTest(final String name) { super(name); } public void testParseClient() { for (int i = 0; i < TESTS.length; i += 2) { assertEquals("Failed to parse", TESTS[i + 1], FTPClient.parsePathname(TESTS[i])); } } public void testParsePassiveModeReplyForLocalAddressWithNatWorkaround() throws Exception { final FTPClient client = new PassiveNatWorkAroundLocalClient("8.8.8.8"); client.setIpAddressFromPasvResponse(true); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertEquals("8.8.8.8", client.getPassiveHost()); client.setIpAddressFromPasvResponse(false); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertNull(client.getPassiveHost()); } @SuppressWarnings("deprecation") // testing deprecated code public void testParsePassiveModeReplyForLocalAddressWithNatWorkaroundDisabled() throws Exception { final FTPClient client = new PassiveNatWorkAroundLocalClient("8.8.8.8"); client.setPassiveNatWorkaround(false); client.setIpAddressFromPasvResponse(true); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertEquals("172.16.204.138", client.getPassiveHost()); client.setIpAddressFromPasvResponse(false); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertNull(client.getPassiveHost()); } public void testParsePassiveModeReplyForLocalAddressWithoutNatWorkaroundStrategy() throws Exception { final FTPClient client = new PassiveNatWorkAroundLocalClient("8.8.8.8"); client.setPassiveNatWorkaroundStrategy(null); client.setIpAddressFromPasvResponse(true); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertEquals("172.16.204.138", client.getPassiveHost()); client.setIpAddressFromPasvResponse(false); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertNull(client.getPassiveHost()); } public void testParsePassiveModeReplyForLocalAddressWithSimpleNatWorkaroundStrategy() throws Exception { final FTPClient client = new PassiveNatWorkAroundLocalClient("8.8.8.8"); client.setPassiveNatWorkaroundStrategy(hostname -> "4.4.4.4"); client.setIpAddressFromPasvResponse(true); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertEquals("4.4.4.4", client.getPassiveHost()); client.setIpAddressFromPasvResponse(false); client._parsePassiveModeReply("227 Entering Passive Mode (172,16,204,138,192,22)."); assertNull(client.getPassiveHost()); } public void testParsePassiveModeReplyForNonLocalAddressWithNatWorkaround() throws Exception { final FTPClient client = new PassiveNatWorkAroundLocalClient("8.8.8.8"); client.setIpAddressFromPasvResponse(true); client._parsePassiveModeReply("227 Entering Passive Mode (8,8,4,4,192,22)."); assertEquals("8.8.4.4", client.getPassiveHost()); client.setIpAddressFromPasvResponse(false); client._parsePassiveModeReply("227 Entering Passive Mode (8,8,4,4,192,22)."); assertNull(client.getPassiveHost()); } @SuppressWarnings("deprecation") // testing deprecated code public void testParsePassiveModeReplyForNonLocalAddressWithNatWorkaroundDisabled() throws Exception { final FTPClient client = new PassiveNatWorkAroundLocalClient("8.8.8.8"); client.setPassiveNatWorkaround(false); client.setIpAddressFromPasvResponse(true); client._parsePassiveModeReply("227 Entering Passive Mode (8,8,4,4,192,22)."); assertEquals("8.8.4.4", client.getPassiveHost()); client.setIpAddressFromPasvResponse(false); client._parsePassiveModeReply("227 Entering Passive Mode (8,8,4,4,192,22)."); assertNull(client.getPassiveHost()); } public void testParsePassiveModeReplyForNonLocalAddressWithoutNatWorkaroundStrategy() throws Exception { final FTPClient client = new PassiveNatWorkAroundLocalClient("8.8.8.8"); client.setPassiveNatWorkaroundStrategy(null); client.setIpAddressFromPasvResponse(true); client._parsePassiveModeReply("227 Entering Passive Mode (8,8,4,4,192,22)."); assertEquals("8.8.4.4", client.getPassiveHost()); client.setIpAddressFromPasvResponse(false); client._parsePassiveModeReply("227 Entering Passive Mode (8,8,4,4,192,22)."); assertNull(client.getPassiveHost()); } public void testParserCachingNullKey() throws Exception { final LocalClient client = new LocalClient(); client.setSystemType(FTPClientConfig.SYST_UNIX); assertNull(client.getEntryParser()); client.createParser(null); final FTPFileEntryParser entryParser = client.getEntryParser(); assertNotNull(entryParser); client.createParser(null); assertSame(entryParser, client.getEntryParser()); // parser was cached client.setSystemType(FTPClientConfig.SYST_NT); client.createParser(null); assertSame(entryParser, client.getEntryParser()); // parser was cached } public void testParserCachingWithKey() throws Exception { final FTPClient client = new FTPClient(); assertNull(client.getEntryParser()); client.createParser(FTPClientConfig.SYST_UNIX); final FTPFileEntryParser entryParserSYST = client.getEntryParser(); assertNotNull(entryParserSYST); client.createParser(FTPClientConfig.SYST_UNIX); assertSame(entryParserSYST, client.getEntryParser()); // the previous entry was cached client.createParser(FTPClientConfig.SYST_VMS); final FTPFileEntryParser entryParserVMS = client.getEntryParser(); assertNotSame(entryParserSYST, entryParserVMS); // the previous entry was replaced client.createParser(FTPClientConfig.SYST_VMS); assertSame(entryParserVMS, client.getEntryParser()); // the previous entry was cached client.createParser(FTPClientConfig.SYST_UNIX); // revert assertNotSame(entryParserVMS, client.getEntryParser()); // the previous entry was replaced } public void testUnparseableFiles() throws Exception { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write("-rwxr-xr-x 2 root root 4096 Mar 2 15:13 zxbox".getBytes()); baos.write(new byte[] { '\r', '\n' }); baos.write("zrwxr-xr-x 2 root root 4096 Mar 2 15:13 zxbox".getBytes()); baos.write(new byte[] { '\r', '\n' }); final FTPFileEntryParser parser = new UnixFTPEntryParser(); final FTPClientConfig config = new FTPClientConfig(); FTPListParseEngine engine = new FTPListParseEngine(parser, config); config.setUnparseableEntries(false); engine.readServerList(new ByteArrayInputStream(baos.toByteArray()), null); // use default encoding FTPFile[] files = engine.getFiles(); assertEquals(1, files.length); config.setUnparseableEntries(true); engine = new FTPListParseEngine(parser, config); engine.readServerList(new ByteArrayInputStream(baos.toByteArray()), null); // use default encoding files = engine.getFiles(); assertEquals(2, files.length); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/FTPCommandTest.java000066400000000000000000000021561434047722200321740ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.ftp; import junit.framework.TestCase; public class FTPCommandTest extends TestCase { public FTPCommandTest(final String name) { super(name); } @SuppressWarnings("deprecation") // test of deprecated code public void testArray() { FTPCommand.checkArray(); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/FTPSClientTest.java000066400000000000000000000152471434047722200321640ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.net.SocketException; import java.time.Instant; import java.util.Calendar; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** * Tests {@link FTPSClient}. *

* To get our test cert to work on Java 11, this test must be run with: *

* *
 * -Djdk.tls.client.protocols="TLSv1.1"
 * 
*

* This test does the above programmatically. *

*/ @RunWith(Parameterized.class) public class FTPSClientTest extends AbstractFtpsTest { private static final String USER_PROPS_RES = "org/apache/commons/net/ftpsserver/users.properties"; private static final String SERVER_JKS_RES = "org/apache/commons/net/ftpsserver/ftpserver.jks"; @BeforeClass public static void setupServer() throws Exception { setupServer(IMPLICIT, USER_PROPS_RES, SERVER_JKS_RES, "target/test-classes/org/apache/commons/net/test-data"); } @Parameters(name = "endpointCheckingEnabled={0}") public static Boolean[] testConstructurData() { return new Boolean[] { Boolean.FALSE, Boolean.TRUE }; } public FTPSClientTest(final boolean endpointCheckingEnabled) { super(endpointCheckingEnabled, null, null); } @Test(timeout = TEST_TIMEOUT) public void testHasFeature() throws SocketException, IOException { trace(">>testHasFeature"); loginClient().disconnect(); trace("<>testListFilesPathNameEmpty"); testListFiles(""); trace("<>testListFilesPathNameJunk"); testListFiles(" Junk "); trace("<>testListFilesPathNameNull"); testListFiles(null); trace("<>testListFilesPathNameRoot"); testListFiles("/"); trace("<>testMdtmCalendar"); testMdtmCalendar("/file.txt"); trace("<>testMdtmFile"); testMdtmFile("/file.txt"); trace("<>testMdtmInstant"); testMdtmInstant("/file.txt"); trace("<>testOpenClose"); final FTPSClient ftpsClient = loginClient(); try { assertTrue(ftpsClient.hasFeature("MODE")); assertTrue(ftpsClient.hasFeature(FTPCmd.MODE)); } finally { ftpsClient.disconnect(); } trace("<>testRetrieveFilePathNameRoot"); retrieveFile("/file.txt"); trace("< clasz = ListingFunctionalTest.class; final Method[] methods = clasz.getDeclaredMethods(); final TestSuite allSuites = new TestSuite("FTP Listing Functional Test Suite"); for (final String[] element : testData) { final TestSuite suite = new TestSuite(element[VALID_PARSERKEY] + " @ " + element[HOSTNAME]); for (final Method method : methods) { if (method.getName().startsWith("test")) { suite.addTest(new ListingFunctionalTest(method.getName(), element)); } } allSuites.addTest(suite); } return allSuites; } private FTPClient client; private final String hostName; private final String invalidParserKey; private final String invalidPath; private final String validFilename; private final String validParserKey; private final String validPath; private final String pwdPath; public ListingFunctionalTest(final String arg0, final String[] settings) { super(arg0); invalidParserKey = settings[INVALID_PARSERKEY]; validParserKey = settings[VALID_PARSERKEY]; invalidPath = settings[INVALID_PATH]; validFilename = settings[VALID_FILENAME]; validPath = settings[VALID_PATH]; pwdPath = settings[PATH_PWD]; hostName = settings[HOSTNAME]; } private boolean findByName(final List fileList, final String string) { boolean found = false; final Iterator iter = fileList.iterator(); while (iter.hasNext() && !found) { final Object element = iter.next(); if (element instanceof FTPFile) { final FTPFile file = (FTPFile) element; found = file.getName().equals(string); } else { final String fileName = (String) element; found = fileName.endsWith(string); } } return found; } /* * @see TestCase#setUp() */ @Override protected void setUp() throws Exception { super.setUp(); client = new FTPClient(); client.connect(hostName); client.login("anonymous", "anonymous"); client.enterLocalPassiveMode(); // client.addProtocolCommandListener(new PrintCommandListener(System.out)); } /* * @see TestCase#tearDown() */ @Override protected void tearDown() throws Exception { try { client.logout(); } catch (final IOException e) { e.printStackTrace(); } if (client.isConnected()) { client.disconnect(); } client = null; super.tearDown(); } /* * Test for FTPListParseEngine initiateListParsing() */ public void testInitiateListParsing() throws IOException { client.changeWorkingDirectory(validPath); final FTPListParseEngine engine = client.initiateListParsing(); final List files = Arrays.asList(engine.getNext(25)); assertTrue(files.toString(), findByName(files, validFilename)); } /* * Test for FTPListParseEngine initiateListParsing(String, String) */ public void testInitiateListParsingWithPath() throws IOException { final FTPListParseEngine engine = client.initiateListParsing(validParserKey, validPath); final List files = Arrays.asList(engine.getNext(25)); assertTrue(files.toString(), findByName(files, validFilename)); } /* * Test for FTPListParseEngine initiateListParsing(String) */ public void testInitiateListParsingWithPathAndAutodetection() throws IOException { final FTPListParseEngine engine = client.initiateListParsing(validPath); final List files = Arrays.asList(engine.getNext(25)); assertTrue(files.toString(), findByName(files, validFilename)); } /* * Test for FTPListParseEngine initiateListParsing(String) */ public void testInitiateListParsingWithPathAndAutodetectionButEmpty() throws IOException { final FTPListParseEngine engine = client.initiateListParsing(invalidPath); assertFalse(engine.hasNext()); } /* * Test for FTPListParseEngine initiateListParsing(String, String) */ public void testInitiateListParsingWithPathAndIncorrectParser() throws IOException { final FTPListParseEngine engine = client.initiateListParsing(invalidParserKey, invalidPath); assertFalse(engine.hasNext()); } /* * Test for FTPFile[] listFiles(String, String) */ public void testListFiles() throws IOException { final FTPClientConfig config = new FTPClientConfig(validParserKey); client.configure(config); final List files = Arrays.asList(client.listFiles(validPath)); assertTrue(files.toString(), findByName(files, validFilename)); } public void testListFilesWithAutodection() throws IOException { client.changeWorkingDirectory(validPath); final List files = Arrays.asList(client.listFiles()); assertTrue(files.toString(), findByName(files, validFilename)); } /* * Test for FTPFile[] listFiles(String, String) */ public void testListFilesWithIncorrectParser() throws IOException { final FTPClientConfig config = new FTPClientConfig(invalidParserKey); client.configure(config); final FTPFile[] files = client.listFiles(validPath); assertNotNull(files); // This may well fail, e.g. window parser for VMS listing assertArrayEquals("Expected empty array: " + Arrays.toString(files), new FTPFile[] {}, files); } /* * Test for FTPFile[] listFiles(String) */ public void testListFilesWithPathAndAutodectionButEmpty() throws IOException { final FTPFile[] files = client.listFiles(invalidPath); assertEquals(0, files.length); } /* * Test for FTPFile[] listFiles(String) */ public void testListFilesWithPathAndAutodetection() throws IOException { final List files = Arrays.asList(client.listFiles(validPath)); assertTrue(files.toString(), findByName(files, validFilename)); } /* * Test for String[] listNames() */ public void testListNames() throws IOException { client.changeWorkingDirectory(validPath); final String[] names = client.listNames(); assertNotNull(names); final List lnames = Arrays.asList(names); assertTrue(lnames.toString(), lnames.contains(validFilename)); } /* * Test for String[] listNames(String) */ public void testListNamesWithPath() throws IOException { final String[] listNames = client.listNames(validPath); assertNotNull("listNames not null", listNames); final List names = Arrays.asList(listNames); assertTrue(names.toString(), findByName(names, validFilename)); } public void testListNamesWithPathButEmpty() throws IOException { final String[] names = client.listNames(invalidPath); assertNull(names); } public void testPrintWorkingDirectory() throws IOException { client.changeWorkingDirectory(validPath); final String pwd = client.printWorkingDirectory(); assertEquals(pwdPath, pwd); } } NoProtocolSslConfigurationProxy.java000066400000000000000000000042451434047722200357000ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.security.GeneralSecurityException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import org.apache.ftpserver.ssl.ClientAuth; import org.apache.ftpserver.ssl.SslConfiguration; /** * see https://issues.apache.org/jira/browse/FTPSERVER-491 */ public class NoProtocolSslConfigurationProxy implements SslConfiguration { private final SslConfiguration sslConfiguration; public NoProtocolSslConfigurationProxy(final SslConfiguration sslConfiguration) { this.sslConfiguration = sslConfiguration; } @Override public ClientAuth getClientAuth() { return this.sslConfiguration.getClientAuth(); } @Override public String[] getEnabledCipherSuites() { return this.sslConfiguration.getEnabledCipherSuites(); } @Override public String[] getEnabledProtocols() { return null; } @Override public SSLSocketFactory getSocketFactory() throws GeneralSecurityException { return this.sslConfiguration.getSocketFactory(); } @Override public SSLContext getSSLContext() throws GeneralSecurityException { return this.sslConfiguration.getSSLContext(); } @Override public SSLContext getSSLContext(final String protocol) throws GeneralSecurityException { return this.sslConfiguration.getSSLContext(protocol); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/TestConnectTimeout.java000066400000000000000000000033251434047722200332030ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp; import java.io.IOException; import java.net.ConnectException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import junit.framework.TestCase; /** * Test the socket connect timeout functionality */ public class TestConnectTimeout extends TestCase { public void testConnectTimeout() throws SocketException, IOException { final FTPClient client = new FTPClient(); client.setConnectTimeout(1000); try { // Connect to a valid host on a bogus port // TODO use a local server if possible client.connect("www.apache.org", 1234); fail("Expecting an Exception"); } catch (final ConnectException | SocketTimeoutException | UnknownHostException ue) { // Not much we can do about this, we may be firewalled assertTrue(true); } } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/000077500000000000000000000000001434047722200300315ustar00rootroot00000000000000CompositeFTPParseTestFramework.java000066400000000000000000000074221434047722200366470ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** */ public abstract class CompositeFTPParseTestFramework extends FTPParseTestFramework { public CompositeFTPParseTestFramework(final String name) { super(name); } @Override protected String[] getBadListing() { return getBadListings()[0]; } /** * Method getBadListing. Implementors must provide multiple listing that contains failures and must force the composite parser to switch the FtpEntryParser * * @return String[] */ protected abstract String[][] getBadListings(); @Override protected String[] getGoodListing() { return getGoodListings()[0]; } /** * Method getGoodListing. Implementors must provide multiple listing that passes and must force the composite parser to switch the FtpEntryParser * * @return String[] */ protected abstract String[][] getGoodListings(); /* * (non-Javadoc) * * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testGoodListing() */ @Override public void testBadListing() { final String badsamples[][] = getBadListings(); for (final String[] badsample : badsamples) { final FTPFileEntryParser parser = getParser(); for (final String test : badsample) { final FTPFile f = parser.parseFTPEntry(test); assertNull("Should have Failed to parse " + test, nullFileOrNullDate(f)); doAdditionalBadTests(test, f); } } } /* * (non-Javadoc) * * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testGoodListing() */ public void testConsistentListing() { final String goodsamples[][] = getGoodListings(); for (final String[] goodsample : goodsamples) { final FTPFileEntryParser parser = getParser(); for (final String test : goodsample) { final FTPFile f = parser.parseFTPEntry(test); assertNotNull("Failed to parse " + test, f); doAdditionalGoodTests(test, f); } } } // even though all these listings are good using one parser // or the other, this tests that a parser that has succeeded // on one format will fail if another format is substituted. public void testInconsistentListing() { final String goodsamples[][] = getGoodListings(); final FTPFileEntryParser parser = getParser(); for (int i = 0; i < goodsamples.length; i++) { final String test = goodsamples[i][0]; final FTPFile f = parser.parseFTPEntry(test); switch (i) { case 0: assertNotNull("Failed to parse " + test, f); break; case 1: assertNull("Should have failed to parse " + test, f); break; } } } } DefaultFTPFileEntryParserFactoryTest.java000066400000000000000000000170421434047722200377460ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFileEntryParser; import junit.framework.TestCase; public class DefaultFTPFileEntryParserFactoryTest extends TestCase { private void checkParserClass(final FTPFileEntryParserFactory fact, final String key, final Class expected) { final FTPClientConfig config = key == null ? new FTPClientConfig() : new FTPClientConfig(key); final FTPFileEntryParser parser = fact.createFileEntryParser(config); assertNotNull(parser); assertTrue("Expected " + expected.getCanonicalName() + " got " + parser.getClass().getCanonicalName(), expected.isInstance(parser)); } public void testDefaultParserFactory() { final DefaultFTPFileEntryParserFactory factory = new DefaultFTPFileEntryParserFactory(); FTPFileEntryParser parser = factory.createFileEntryParser("unix"); assertTrue(parser instanceof UnixFTPEntryParser); parser = factory.createFileEntryParser("UNIX"); assertTrue(parser instanceof UnixFTPEntryParser); assertFalse(((UnixFTPEntryParser) parser).trimLeadingSpaces); parser = factory.createFileEntryParser("UNIX_LTRIM"); assertTrue(parser instanceof UnixFTPEntryParser); assertTrue(((UnixFTPEntryParser) parser).trimLeadingSpaces); parser = factory.createFileEntryParser("Unix"); assertTrue(parser instanceof UnixFTPEntryParser); parser = factory.createFileEntryParser("EnterpriseUnix"); assertTrue(parser instanceof UnixFTPEntryParser); assertFalse(parser instanceof EnterpriseUnixFTPEntryParser); // works because contains the expression "Unix" parser = factory.createFileEntryParser("UnixFTPEntryParser"); assertTrue(parser instanceof UnixFTPEntryParser); try { parser = factory.createFileEntryParser("NT"); fail("Exception should have been thrown. \"NT\" is not a recognized key"); } catch (final ParserInitializationException pie) { assertNull(pie.getCause()); assertTrue(pie.getMessage() + "should contain 'Unknown parser type:'", pie.getMessage().contains("Unknown parser type:")); } parser = factory.createFileEntryParser("WindowsNT"); assertTrue(parser instanceof CompositeFileEntryParser); parser = factory.createFileEntryParser("ThigaVMSaMaJig"); assertTrue(parser instanceof VMSFTPEntryParser); parser = factory.createFileEntryParser("OS/2"); assertTrue(parser instanceof OS2FTPEntryParser); parser = factory.createFileEntryParser("OS/400"); assertTrue(parser instanceof CompositeFileEntryParser); parser = factory.createFileEntryParser("AS/400"); assertTrue(parser instanceof CompositeFileEntryParser); // Added test to make sure it handles the Unix systems that were // compiled with OS as "UNKNOWN". This test validates that the // check is case-insensitive. parser = factory.createFileEntryParser("UNKNOWN Type: L8"); try { parser = factory.createFileEntryParser("OS2FTPFileEntryParser"); fail("Exception should have been thrown. \"OS2FTPFileEntryParser\" is not a recognized key"); } catch (final ParserInitializationException pie) { assertNull(pie.getCause()); } parser = factory.createFileEntryParser("org.apache.commons.net.ftp.parser.OS2FTPEntryParser"); assertTrue(parser instanceof OS2FTPEntryParser); try { factory.createFileEntryParser("org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory"); fail("Exception should have been thrown. \"DefaultFTPFileEntryParserFactory\" does not implement FTPFileEntryParser"); } catch (final ParserInitializationException pie) { final Throwable root = pie.getCause(); assertTrue(root instanceof ClassCastException); } try { // Class exists, but is an interface factory.createFileEntryParser("org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory"); fail("ParserInitializationException should have been thrown."); } catch (final ParserInitializationException pie) { final Throwable root = pie.getCause(); assertTrue(root instanceof InstantiationException); } try { // Class exists, but is abstract factory.createFileEntryParser("org.apache.commons.net.ftp.FTPFileEntryParserImpl"); fail("ParserInitializationException should have been thrown."); } catch (final ParserInitializationException pie) { final Throwable root = pie.getCause(); assertTrue(root instanceof InstantiationException); } } public void testDefaultParserFactoryConfig() throws Exception { final DefaultFTPFileEntryParserFactory factory = new DefaultFTPFileEntryParserFactory(); try { factory.createFileEntryParser((FTPClientConfig) null); fail("Expected NullPointerException"); } catch (final NullPointerException npe) { // expected } checkParserClass(factory, null, UnixFTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_OS400, OS400FTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_AS400, CompositeFileEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_L8, UnixFTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_MVS, MVSFTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_NETWARE, NetwareFTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_NT, NTFTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_OS2, OS2FTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_UNIX, UnixFTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_VMS, VMSFTPEntryParser.class); checkParserClass(factory, FTPClientConfig.SYST_MACOS_PETER, MacOsPeterFTPEntryParser.class); checkParserClass(factory, "WINDOWS", NTFTPEntryParser.class); // Same as SYST_NT // This is the way it works at present; config matching is exact checkParserClass(factory, "Windows", CompositeFileEntryParser.class); checkParserClass(factory, "OS/400", OS400FTPEntryParser.class); // Same as SYST_OS400 // This is the way it works at present; config matching is exact checkParserClass(factory, "OS/400 v1", CompositeFileEntryParser.class); // Note: exact matching via config is the only way to generate NTFTPEntryParser and OS400FTPEntryParser // using DefaultFTPFileEntryParserFactory } } DownloadListings.java000066400000000000000000000114571434047722200341110ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.ftp.parser; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Reader; import java.net.Socket; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPCmd; import org.apache.commons.net.io.Util; /** * Sample class to download LIST and MLSD listings from list of ftp sites. */ public class DownloadListings extends FTPClient { // Also used by MLDSComparison static final String DOWNLOAD_DIR = "target/ftptest"; public static void main(final String[] args) throws Exception { String host;// = "ftp.funet.fi"; final int port = 21; String path;// = "/"; new File(DOWNLOAD_DIR).mkdirs(); final DownloadListings self = new DownloadListings(); final OutputStream os = new FileOutputStream(new File(DOWNLOAD_DIR, "session.log")); self.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(os), true)); final Reader is = new FileReader("mirrors.list"); final BufferedReader rdr = new BufferedReader(is); String line; while ((line = rdr.readLine()) != null) { if (line.startsWith("ftp")) { final String[] parts = line.split("\\s+"); final String target = parts[2]; host = target.substring("ftp://".length()); final int slash = host.indexOf('/'); path = host.substring(slash); host = host.substring(0, slash); System.out.println(host + " " + path); if (self.open(host, port)) { try { self.info(); self.download(path, FTPCmd.LIST, new File(DOWNLOAD_DIR, host + "_list.txt")); self.download(path, FTPCmd.MLSD, new File(DOWNLOAD_DIR, host + "_mlsd.txt")); } catch (final Exception e) { e.printStackTrace(); } finally { self.disconnect(); } self.removeProtocolCommandListener(self.listener); self.out.close(); } } } os.close(); rdr.close(); } private PrintCommandListener listener; private PrintWriter out; private void download(final String path, final FTPCmd command, final File fileName) throws Exception { final Socket socket; if ((socket = _openDataConnection_(command, getListArguments(path))) == null) { System.out.println(getReplyString()); return; } final InputStream inputStream = socket.getInputStream(); final OutputStream outputStream = new FileOutputStream(fileName); Util.copyStream(inputStream, outputStream); inputStream.close(); socket.close(); outputStream.close(); if (!completePendingCommand()) { System.out.println(getReplyString()); } } private void info() throws IOException { syst(); help(); feat(); removeProtocolCommandListener(listener); } private boolean open(final String host, final int port) throws Exception { System.out.println("Connecting to " + host); out = new PrintWriter(new FileWriter(new File(DOWNLOAD_DIR, host + "_info.txt"))); listener = new PrintCommandListener(out); addProtocolCommandListener(listener); setConnectTimeout(30000); try { connect(host, port); } catch (final Exception e) { System.out.println(e); return false; } enterLocalPassiveMode(); // this is reset by connect System.out.println("Logging in to " + host); return login("anonymous", "user@localhost"); } } EnterpriseUnixFTPEntryParserTest.java000066400000000000000000000177531434047722200372270ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.time.Instant; import java.time.Month; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.TimeZone; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** * Tests the EnterpriseUnixFTPEntryParser * */ public class EnterpriseUnixFTPEntryParserTest extends FTPParseTestFramework { private static final String[] BADSAMPLES = { "zrwxr-xr-x 2 root root 4096 Mar 2 15:13 zxbox", "dxrwr-xr-x 2 root root 4096 Aug 24 2001 zxjdbc", "drwxr-xr-x 2 root root 4096 Jam 4 00:03 zziplib", "drwxr-xr-x 2 root 99 4096 Feb 23 30:01 zzplayer", "drwxr-xr-x 2 root root 4096 Aug 36 2001 zztpp", "-rw-r--r-- 1 14 staff 80284 Aug 22 zxJDBC-1.2.3.tar.gz", "-rw-r--r-- 1 14 staff 119:26 Aug 22 2000 zxJDBC-1.2.3.zip", "-rw-r--r-- 1 ftp no group 83853 Jan 22 2001 zxJDBC-1.2.4.tar.gz", "-rw-r--r-- 1ftp nogroup 126552 Jan 22 2001 zxJDBC-1.2.4.zip", "-rw-r--r-- 1 root root 111325 Apr -7 18:79 zxJDBC-2.0.1b1.tar.gz", "drwxr-xr-x 2 root root 4096 Mar 2 15:13 zxbox", "drwxr-xr-x 1 usernameftp 512 Jan 29 23:32 prog", "drwxr-xr-x 2 root root 4096 Aug 24 2001 zxjdbc", "drwxr-xr-x 2 root root 4096 Jan 4 00:03 zziplib", "drwxr-xr-x 2 root 99 4096 Feb 23 2001 zzplayer", "drwxr-xr-x 2 root root 4096 Aug 6 2001 zztpp", "-rw-r--r-- 1 14 staff 80284 Aug 22 2000 zxJDBC-1.2.3.tar.gz", "-rw-r--r-- 1 14 staff 119926 Aug 22 2000 zxJDBC-1.2.3.zip", "-rw-r--r-- 1 ftp nogroup 83853 Jan 22 2001 zxJDBC-1.2.4.tar.gz", "-rw-r--r-- 1 ftp nogroup 126552 Jan 22 2001 zxJDBC-1.2.4.zip", "-rw-r--r-- 1 root root 111325 Apr 27 2001 zxJDBC-2.0.1b1.tar.gz", "-rw-r--r-- 1 root root 190144 Apr 27 2001 zxJDBC-2.0.1b1.zip", "drwxr-xr-x 2 root root 4096 Aug 26 20 zztpp", "drwxr-xr-x 2 root root 4096 Aug 26 201 zztpp", "drwxr-xr-x 2 root root 4096 Aug 26 201O zztpp", // OH not zero }; private static final String[] GOODSAMPLES = { "-C--E-----FTP B QUA1I1 18128 41 Aug 12 13:56 QUADTEST", "-C--E-----FTP A QUA1I1 18128 41 Aug 12 13:56 QUADTEST2", "-C--E-----FTP A QUA1I1 18128 41 Apr 1 2014 QUADTEST3" }; /** * Creates a new EnterpriseUnixFTPEntryParserTest object. * * @param name Test name. */ public EnterpriseUnixFTPEntryParserTest(final String name) { super(name); } /** * Method checkPermisions. Verify that the parser does NOT set the permissions. * * @param dir */ private void checkPermisions(final FTPFile dir) { assertFalse("Owner should not have read permission.", dir.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Owner should not have write permission.", dir.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)); assertFalse("Owner should not have execute permission.", dir.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertFalse("Group should not have read permission.", dir.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Group should not have write permission.", dir.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)); assertFalse("Group should not have execute permission.", dir.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertFalse("World should not have read permission.", dir.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("World should not have write permission.", dir.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)); assertFalse("World should not have execute permission.", dir.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)); } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#getBadListing() */ @Override protected String[] getBadListing() { return BADSAMPLES; } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#getGoodListing() */ @Override protected String[] getGoodListing() { return GOODSAMPLES; } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#getParser() */ @Override protected FTPFileEntryParser getParser() { return new EnterpriseUnixFTPEntryParser(); } @Override public void testDefaultPrecision() { testPrecision("-C--E-----FTP B QUA1I1 18128 5000000000 Aug 12 2014 QUADTEST", CalendarUnit.DAY_OF_MONTH); } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testParseFieldsOnDirectory() */ @Override public void testParseFieldsOnDirectory() throws Exception { // Everything is a File for now. } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testParseFieldsOnFile() */ @Override public void testParseFieldsOnFile() throws Exception { // Note: No time zone. final FTPFile ftpFile = getParser().parseFTPEntry("-C--E-----FTP B QUA1I1 18128 5000000000 Aug 12 13:56 QUADTEST"); final Calendar today = Calendar.getInstance(); int year = today.get(Calendar.YEAR); assertTrue("Should be a file.", ftpFile.isFile()); assertEquals("QUADTEST", ftpFile.getName()); assertEquals(5000000000L, ftpFile.getSize()); assertEquals("QUA1I1", ftpFile.getUser()); assertEquals("18128", ftpFile.getGroup()); if (today.get(Calendar.MONTH) < Calendar.AUGUST) { --year; } final Calendar timestamp = ftpFile.getTimestamp(); assertEquals(year, timestamp.get(Calendar.YEAR)); assertEquals(Calendar.AUGUST, timestamp.get(Calendar.MONTH)); assertEquals(12, timestamp.get(Calendar.DAY_OF_MONTH)); assertEquals(13, timestamp.get(Calendar.HOUR_OF_DAY)); assertEquals(56, timestamp.get(Calendar.MINUTE)); assertEquals(0, timestamp.get(Calendar.SECOND)); // No time zone -> local. final TimeZone timeZone = TimeZone.getDefault(); assertEquals(timeZone, timestamp.getTimeZone()); checkPermisions(ftpFile); final Instant instant = ftpFile.getTimestampInstant(); final ZonedDateTime zDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of(timeZone.getID())); assertEquals(year, zDateTime.getYear()); assertEquals(Month.AUGUST, zDateTime.getMonth()); assertEquals(12, zDateTime.getDayOfMonth()); assertEquals(13, zDateTime.getHour()); assertEquals(56, zDateTime.getMinute()); assertEquals(0, zDateTime.getSecond()); } @Override public void testRecentPrecision() { testPrecision("-C--E-----FTP B QUA1I1 18128 5000000000 Aug 12 13:56 QUADTEST", CalendarUnit.MINUTE); } } FTPConfigEntryParserTest.java000066400000000000000000000146471434047722200354470ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.SimpleDateFormat; import java.util.Calendar; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; import junit.framework.TestCase; /** * This is a simple TestCase that tests entry parsing using the new FTPClientConfig mechanism. The normal FTPClient cannot handle the different date formats in * these entries, however using a configurable format, we can handle it easily. * * The original system presenting this issue was an AIX system - see bug #27437 for details. * */ public class FTPConfigEntryParserTest extends TestCase { private final SimpleDateFormat df = new SimpleDateFormat(); /** * This is a new format reported on the mailing lists. Parsing this kind of entry necessitated changing the regex in the parser. * */ public void testParseEntryWithSymlink() { final FTPClientConfig config = new FTPClientConfig(FTPClientConfig.SYST_UNIX); config.setDefaultDateFormatStr("yyyy-MM-dd HH:mm"); final UnixFTPEntryParser parser = new UnixFTPEntryParser(); parser.configure(config); final FTPFile f = parser.parseFTPEntry("lrwxrwxrwx 1 neeme neeme 23 2005-03-02 18:06 macros"); assertNotNull("Could not parse entry.", f); assertFalse("Is not a directory.", f.isDirectory()); assertTrue("Is a symbolic link", f.isSymbolicLink()); assertTrue("Should have user read permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)); assertTrue("Should have user write permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have user execute permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertTrue("Should have group read permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)); assertTrue("Should have group write permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have group execute permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertTrue("Should have world read permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)); assertTrue("Should have world write permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have world execute permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertEquals(1, f.getHardLinkCount()); assertEquals("neeme", f.getUser()); assertEquals("neeme", f.getGroup()); assertEquals("macros", f.getName()); assertEquals(23, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DAY_OF_MONTH, 2); cal.set(Calendar.HOUR_OF_DAY, 18); cal.set(Calendar.MINUTE, 06); cal.set(Calendar.SECOND, 0); cal.set(Calendar.YEAR, 2005); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } public void testParseFieldsOnAIX() { // Set a date format for this server type final FTPClientConfig config = new FTPClientConfig(FTPClientConfig.SYST_UNIX); config.setDefaultDateFormatStr("dd MMM HH:mm"); final UnixFTPEntryParser parser = new UnixFTPEntryParser(); parser.configure(config); final FTPFile ftpFile = parser.parseFTPEntry("-rw-r----- 1 ravensm sca 814 02 Mar 16:27 ZMIR2.m"); assertNotNull("Could not parse entry.", ftpFile); assertFalse("Is not a directory.", ftpFile.isDirectory()); assertTrue("Should have user read permission.", ftpFile.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)); assertTrue("Should have user write permission.", ftpFile.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)); assertFalse("Should NOT have user execute permission.", ftpFile.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertTrue("Should have group read permission.", ftpFile.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Should NOT have group write permission.", ftpFile.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)); assertFalse("Should NOT have group execute permission.", ftpFile.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertFalse("Should NOT have world read permission.", ftpFile.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Should NOT have world write permission.", ftpFile.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)); assertFalse("Should NOT have world execute permission.", ftpFile.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertEquals(1, ftpFile.getHardLinkCount()); assertEquals("ravensm", ftpFile.getUser()); assertEquals("sca", ftpFile.getGroup()); assertEquals("ZMIR2.m", ftpFile.getName()); assertEquals(814, ftpFile.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DAY_OF_MONTH, 2); cal.set(Calendar.HOUR_OF_DAY, 16); cal.set(Calendar.MINUTE, 27); cal.set(Calendar.SECOND, 0); // With no year specified, it defaults to 1970 // TODO this is probably a bug - it should default to the current year cal.set(Calendar.YEAR, 1970); assertEquals(df.format(cal.getTime()), df.format(ftpFile.getTimestamp().getTime())); } } FTPParseTestFramework.java000066400000000000000000000140221434047722200347560ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.SimpleDateFormat; import java.time.Instant; import java.util.Calendar; import java.util.Locale; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; import junit.framework.TestCase; /** */ public abstract class FTPParseTestFramework extends TestCase { // associate Calendar unit ints with a readable string // MUST be listed least significant first, as the routine needs to // find the previous - less significant - entry protected enum CalendarUnit { MILLISECOND(Calendar.MILLISECOND), SECOND(Calendar.SECOND), MINUTE(Calendar.MINUTE), HOUR_OF_DAY(Calendar.HOUR_OF_DAY), DAY_OF_MONTH(Calendar.DAY_OF_MONTH), MONTH(Calendar.MONTH), YEAR(Calendar.YEAR); final int unit; CalendarUnit(final int calUnit) { unit = calUnit; } } private FTPFileEntryParser parser; protected SimpleDateFormat df; /** * @see junit.framework.TestCase#TestCase(String) */ public FTPParseTestFramework(final String name) { super(name); } /** * during processing you could hook here to do additional tests * * @param test raw entry * @param f parsed entry */ protected void doAdditionalBadTests(final String test, final FTPFile f) { } /** * during processing you could hook here to do additional tests * * @param test raw entry * @param f parsed entry */ protected void doAdditionalGoodTests(final String test, final FTPFile f) { } /** * Method getBadListing. Implementors must provide a listing that contains failures. * * @return String[] */ protected abstract String[] getBadListing(); /** * Method getGoodListing. Implementors must provide a listing that passes. * * @return String[] */ protected abstract String[] getGoodListing(); /** * Method getParser. Provide the parser to use for testing. * * @return FTPFileEntryParser */ protected abstract FTPFileEntryParser getParser(); /** * Check if FTPFile entry parsing failed; i.e. if entry is null or date is null. * * @param f FTPFile entry - may be null * @return null if f is null or the date is null */ protected FTPFile nullFileOrNullDate(final FTPFile f) { if (f == null) { return null; } if (f.getTimestamp() == null) { return null; } return f; } @Override protected void setUp() throws Exception { super.setUp(); parser = getParser(); df = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", Locale.US); } public void testBadListing() { final String[] badsamples = getBadListing(); for (final String test : badsamples) { final FTPFile f = parser.parseFTPEntry(test); assertNull("Should have Failed to parse <" + test + ">", nullFileOrNullDate(f)); doAdditionalBadTests(test, f); } } // Force subclasses to test precision public abstract void testDefaultPrecision(); public void testGoodListing() { final String[] goodsamples = getGoodListing(); for (final String test : goodsamples) { final FTPFile f = parser.parseFTPEntry(test); assertNotNull("Failed to parse " + test, f); doAdditionalGoodTests(test, f); } } /** * Method testParseFieldsOnDirectory. Provide a test to show that fields on a directory entry are parsed correctly. * * @throws Exception on error */ public abstract void testParseFieldsOnDirectory() throws Exception; /** * Method testParseFieldsOnFile. Provide a test to show that fields on a file entry are parsed correctly. * * @throws Exception on error */ public abstract void testParseFieldsOnFile() throws Exception; protected void testPrecision(final String listEntry, final CalendarUnit expectedPrecision) { final FTPFile file = getParser().parseFTPEntry(listEntry); assertNotNull("Could not parse " + listEntry, file); final Calendar stamp = file.getTimestamp(); assertNotNull("Failed to parse time in " + listEntry, stamp); final Instant instant = file.getTimestampInstant(); assertNotNull("Failed to parse time in " + listEntry, instant); final int ordinal = expectedPrecision.ordinal(); final CalendarUnit[] values = CalendarUnit.values(); // Check expected unit and all more significant ones are set // This is needed for FTPFile.toFormattedString() to work correctly for (int i = ordinal; i < values.length; i++) { final CalendarUnit unit = values[i]; assertTrue("Expected set " + unit + " in " + listEntry, stamp.isSet(unit.unit)); } // Check previous entry (if any) is not set // This is also needed for FTPFile.toFormattedString() to work correctly if (ordinal > 0) { final CalendarUnit prevUnit = values[ordinal - 1]; assertFalse("Expected not set " + prevUnit + " in " + listEntry, stamp.isSet(prevUnit.unit)); } } public abstract void testRecentPrecision(); } FTPTimestampParserImplTest.java000066400000000000000000000524471434047722200360050ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.text.Format; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import org.apache.commons.net.ftp.FTPClientConfig; import junit.framework.AssertionFailedError; import junit.framework.TestCase; /** * Test the FTPTimestampParser class. */ public class FTPTimestampParserImplTest extends TestCase { private static final int TWO_HOURS_OF_MILLISECONDS = 2 * 60 * 60 * 1000; /* * Check how short date is interpreted at a given time. Check both with and without lenient future dates */ private void checkShortParse(final String msg, final Calendar serverTime, final Calendar input) throws ParseException { checkShortParse(msg, serverTime, input, false); checkShortParse(msg, serverTime, input, true); } /** * Check how short date is interpreted at a given time Check only using specified lenient future dates setting * * @param msg identifying message * @param servertime the time at the server * @param input the time to be converted to a short date, parsed and tested against the full time * @param lenient whether to use lenient mode or not. */ private void checkShortParse(final String msg, final Calendar servertime, final Calendar input, final boolean lenient) throws ParseException { checkShortParse(msg, servertime, input, input, lenient); } /* * Check how short date is interpreted at a given time. Check both with and without lenient future dates */ private void checkShortParse(final String msg, final Calendar serverTime, final Calendar input, final Calendar expected) throws ParseException { checkShortParse(msg, serverTime, input, expected, false); checkShortParse(msg, serverTime, input, expected, true); } /** * Check how short date is interpreted at a given time Check only using specified lenient future dates setting * * @param msg identifying message * @param servertime the time at the server * @param input the time to be converted to a short date and parsed * @param expected the expected result from parsing * @param lenient whether to use lenient mode or not. */ private void checkShortParse(final String msg, final Calendar servertime, final Calendar input, final Calendar expected, final boolean lenient) throws ParseException { final FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); parser.setLenientFutureDates(lenient); final SimpleDateFormat shortFormat = parser.getRecentDateFormat(); // It's expecting this format final String shortDate = shortFormat.format(input.getTime()); final Calendar output = parser.parseTimestamp(shortDate, servertime); final int outyear = output.get(Calendar.YEAR); final int outdom = output.get(Calendar.DAY_OF_MONTH); final int outmon = output.get(Calendar.MONTH); final int inyear = expected.get(Calendar.YEAR); final int indom = expected.get(Calendar.DAY_OF_MONTH); final int inmon = expected.get(Calendar.MONTH); if (indom != outdom || inmon != outmon || inyear != outyear) { final Format longFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm"); fail("Test: '" + msg + "' Server=" + longFormat.format(servertime.getTime()) + ". Failed to parse " + shortDate + (lenient ? " (lenient)" : " (non-lenient)") + " using " + shortFormat.toPattern() + ". Actual " + longFormat.format(output.getTime()) + ". Expected " + longFormat.format(expected.getTime())); } } // This test currently fails, because we assume that short dates are +-6months when parsing Feb 29 public void DISABLEDtestNET446() throws Exception { final GregorianCalendar server = new GregorianCalendar(2001, Calendar.JANUARY, 1, 12, 0); // Note: we use a known leap year for the target date to avoid rounding up final GregorianCalendar input = new GregorianCalendar(2000, Calendar.FEBRUARY, 29); final GregorianCalendar expected = new GregorianCalendar(2000, Calendar.FEBRUARY, 29); checkShortParse("Feb 29th 2000", server, input, expected); } // Test leap year if current year is a leap year public void testFeb29IfLeapYear() throws Exception { final GregorianCalendar now = new GregorianCalendar(); final int thisYear = now.get(Calendar.YEAR); final GregorianCalendar target = new GregorianCalendar(thisYear, Calendar.FEBRUARY, 29); if (now.isLeapYear(thisYear) && now.after(target) && now.before(new GregorianCalendar(thisYear, Calendar.AUGUST, 29))) { checkShortParse("Feb 29th", now, target); } else { System.out.println("Skipping Feb 29 test (not leap year or before Feb 29)"); } } // Test Feb 29 for a known leap year public void testFeb29LeapYear() throws Exception { final int year = 2000; // Use same year for current and short date final GregorianCalendar now = new GregorianCalendar(year, Calendar.APRIL, 1, 12, 0); checkShortParse("Feb 29th 2000", now, new GregorianCalendar(year, Calendar.FEBRUARY, 29)); } public void testFeb29LeapYear2() throws Exception { final int year = 2000; // Use same year for current and short date final GregorianCalendar now = new GregorianCalendar(year, Calendar.MARCH, 1, 12, 0); checkShortParse("Feb 29th 2000", now, new GregorianCalendar(year, Calendar.FEBRUARY, 29)); } // same date feb 29 public void testFeb29LeapYear3() throws Exception { final int year = 2000; // Use same year for current and short date final GregorianCalendar now = new GregorianCalendar(year, Calendar.FEBRUARY, 29, 12, 0); checkShortParse("Feb 29th 2000", now, new GregorianCalendar(year, Calendar.FEBRUARY, 29)); } // future dated Feb 29 public void testFeb29LeapYear4() throws Exception { final int year = 2000; // Use same year for current and short date final GregorianCalendar now = new GregorianCalendar(year, Calendar.FEBRUARY, 28, 12, 0); // Must allow lenient future date here checkShortParse("Feb 29th 2000", now, new GregorianCalendar(year, Calendar.FEBRUARY, 29), true); } // Test Feb 29 for a known non-leap year - should fail public void testFeb29NonLeapYear() { final GregorianCalendar server = new GregorianCalendar(1999, Calendar.APRIL, 1, 12, 0); // Note: we use a known leap year for the target date to avoid rounding up final GregorianCalendar input = new GregorianCalendar(2000, Calendar.FEBRUARY, 29); final GregorianCalendar expected = new GregorianCalendar(1999, Calendar.FEBRUARY, 29); try { checkShortParse("Feb 29th 1999", server, input, expected, true); fail("Should have failed to parse Feb 29th 1999"); } catch (final ParseException pe) { // expected } try { checkShortParse("Feb 29th 1999", server, input, expected, false); fail("Should have failed to parse Feb 29th 1999"); } catch (final ParseException pe) { // expected } } // Lenient mode allows for dates up to 1 day in the future public void testNET444() throws Exception { final FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); parser.setLenientFutureDates(true); final SimpleDateFormat sdf = new SimpleDateFormat(parser.getRecentDateFormatString()); final GregorianCalendar now = new GregorianCalendar(2012, Calendar.FEBRUARY, 28, 12, 0); final GregorianCalendar nowplus1 = new GregorianCalendar(2012, Calendar.FEBRUARY, 28, 13, 0); // Create a suitable short date final String future1 = sdf.format(nowplus1.getTime()); final Calendar parsed1 = parser.parseTimestamp(future1, now); assertEquals(nowplus1.get(Calendar.YEAR), parsed1.get(Calendar.YEAR)); final GregorianCalendar nowplus25 = new GregorianCalendar(2012, Calendar.FEBRUARY, 29, 13, 0); // Create a suitable short date final String future25 = sdf.format(nowplus25.getTime()); final Calendar parsed25 = parser.parseTimestamp(future25, now); assertEquals(nowplus25.get(Calendar.YEAR) - 1, parsed25.get(Calendar.YEAR)); } public void testParseDec31Lenient() throws Exception { final GregorianCalendar now = new GregorianCalendar(2007, Calendar.DECEMBER, 30, 12, 0); checkShortParse("2007-12-30", now, now); // should always work final GregorianCalendar target = (GregorianCalendar) now.clone(); target.add(Calendar.DAY_OF_YEAR, +1); // tomorrow checkShortParse("2007-12-31", now, target, true); } public void testParseJan01() throws Exception { final GregorianCalendar now = new GregorianCalendar(2007, Calendar.JANUARY, 1, 12, 0); checkShortParse("2007-01-01", now, now); // should always work final GregorianCalendar target = new GregorianCalendar(2006, Calendar.DECEMBER, 31, 12, 0); checkShortParse("2006-12-31", now, target, true); checkShortParse("2006-12-31", now, target, false); } public void testParseJan01Lenient() throws Exception { final GregorianCalendar now = new GregorianCalendar(2007, Calendar.DECEMBER, 31, 12, 0); checkShortParse("2007-12-31", now, now); // should always work final GregorianCalendar target = (GregorianCalendar) now.clone(); target.add(Calendar.DAY_OF_YEAR, +1); // tomorrow checkShortParse("2008-1-1", now, target, true); } public void testParser() { // This test requires an English Locale final Locale locale = Locale.getDefault(); try { Locale.setDefault(Locale.ENGLISH); final FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); try { parser.parseTimestamp("feb 22 2002"); } catch (final ParseException e) { fail("failed.to.parse.default"); } try { final Calendar c = parser.parseTimestamp("f\u00e9v 22 2002"); fail("should.have.failed.to.parse.default, but was: " + c.getTime().toString()); } catch (final ParseException e) { // this is the success case } final FTPClientConfig config = new FTPClientConfig(); config.setDefaultDateFormatStr("d MMM yyyy"); config.setRecentDateFormatStr("d MMM HH:mm"); config.setServerLanguageCode("fr"); parser.configure(config); try { parser.parseTimestamp("d\u00e9c 22 2002"); fail("incorrect.field.order"); } catch (final ParseException e) { // this is the success case } try { parser.parseTimestamp("22 d\u00e9c 2002"); } catch (final ParseException e) { fail("failed.to.parse.french"); } try { parser.parseTimestamp("22 dec 2002"); fail("incorrect.language"); } catch (final ParseException e) { // this is the success case } try { parser.parseTimestamp("29 f\u00e9v 2002"); fail("nonexistent.date"); } catch (final ParseException e) { // this is the success case } try { parser.parseTimestamp("22 ao\u00fb 30:02"); fail("bad.hour"); } catch (final ParseException e) { // this is the success case } try { parser.parseTimestamp("22 ao\u00fb 20:74"); fail("bad.minute"); } catch (final ParseException e) { // this is the success case } try { parser.parseTimestamp("28 ao\u00fb 20:02"); } catch (final ParseException e) { fail("failed.to.parse.french.recent"); } } finally { Locale.setDefault(locale); } } public void testParseShortFutureDates1() throws Exception { final GregorianCalendar now = new GregorianCalendar(2001, Calendar.MAY, 30, 12, 0); checkShortParse("2001-5-30", now, now); // should always work final GregorianCalendar target = (GregorianCalendar) now.clone(); target.add(Calendar.DAY_OF_MONTH, 1); checkShortParse("2001-5-30 +1 day", now, target, true); try { checkShortParse("2001-5-30 +1 day", now, target, false); fail("Expected AssertionFailedError"); } catch (final AssertionFailedError pe) { if (pe.getMessage().startsWith("Expected AssertionFailedError")) { // don't swallow our failure throw pe; } } target.add(Calendar.WEEK_OF_YEAR, 1); // checkShortParse("2001-5-30 +1 week",now,target); // target.add(Calendar.WEEK_OF_YEAR, 12); // checkShortParse("2001-5-30 +13 weeks",now,target); // target.add(Calendar.WEEK_OF_YEAR, 13); // checkShortParse("2001-5-30 +26 weeks",now,target); } public void testParseShortFutureDates2() throws Exception { final GregorianCalendar now = new GregorianCalendar(2004, Calendar.AUGUST, 1, 12, 0); checkShortParse("2004-8-1", now, now); // should always work final GregorianCalendar target = (GregorianCalendar) now.clone(); target.add(Calendar.DAY_OF_MONTH, 1); checkShortParse("2004-8-1 +1 day", now, target, true); try { checkShortParse("2004-8-1 +1 day", now, target, false); fail("Expected AssertionFailedError"); } catch (final AssertionFailedError pe) { if (pe.getMessage().startsWith("Expected AssertionFailedError")) { // don't swallow our failure throw pe; } } // target.add(Calendar.WEEK_OF_YEAR, 1); // checkShortParse("2004-8-1 +1 week",now,target); // target.add(Calendar.WEEK_OF_YEAR, 12); // checkShortParse("2004-8-1 +13 weeks",now,target); // target.add(Calendar.WEEK_OF_YEAR, 13); // checkShortParse("2004-8-1 +26 weeks",now,target); } public void testParseShortPastDates1() throws Exception { final GregorianCalendar now = new GregorianCalendar(2001, Calendar.MAY, 30, 12, 0); checkShortParse("2001-5-30", now, now); // should always work final GregorianCalendar target = (GregorianCalendar) now.clone(); target.add(Calendar.WEEK_OF_YEAR, -1); checkShortParse("2001-5-30 -1 week", now, target); target.add(Calendar.WEEK_OF_YEAR, -12); checkShortParse("2001-5-30 -13 weeks", now, target); target.add(Calendar.WEEK_OF_YEAR, -13); checkShortParse("2001-5-30 -26 weeks", now, target); } public void testParseShortPastDates2() throws Exception { final GregorianCalendar now = new GregorianCalendar(2004, Calendar.AUGUST, 1, 12, 0); checkShortParse("2004-8-1", now, now); // should always work final GregorianCalendar target = (GregorianCalendar) now.clone(); target.add(Calendar.WEEK_OF_YEAR, -1); checkShortParse("2004-8-1 -1 week", now, target); target.add(Calendar.WEEK_OF_YEAR, -12); checkShortParse("2004-8-1 -13 weeks", now, target); target.add(Calendar.WEEK_OF_YEAR, -13); checkShortParse("2004-8-1 -26 weeks", now, target); } public void testParseTimestamp() { final Calendar cal = Calendar.getInstance(); cal.add(Calendar.HOUR_OF_DAY, 1); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); final Date anHourFromNow = cal.getTime(); final FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); final SimpleDateFormat sdf = new SimpleDateFormat(parser.getRecentDateFormatString()); final String fmtTime = sdf.format(anHourFromNow); try { final Calendar parsed = parser.parseTimestamp(fmtTime); // since the timestamp is ahead of now (by one hour), // this must mean the file's date refers to a year ago. assertEquals("test.roll.back.year", 1, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); } catch (final ParseException e) { fail("Unable to parse"); } } public void testParseTimestampAcrossTimeZones() { final Calendar cal = Calendar.getInstance(); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); cal.add(Calendar.HOUR_OF_DAY, 1); final Date anHourFromNow = cal.getTime(); cal.add(Calendar.HOUR_OF_DAY, 2); final Date threeHoursFromNow = cal.getTime(); cal.add(Calendar.HOUR_OF_DAY, -2); final FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); // assume we are FTPing a server in Chicago, two hours ahead of // L. A. final FTPClientConfig config = new FTPClientConfig(FTPClientConfig.SYST_UNIX); config.setDefaultDateFormatStr(FTPTimestampParser.DEFAULT_SDF); config.setRecentDateFormatStr(FTPTimestampParser.DEFAULT_RECENT_SDF); // 2 hours difference config.setServerTimeZoneId("America/Chicago"); config.setLenientFutureDates(false); // NET-407 parser.configure(config); final SimpleDateFormat sdf = (SimpleDateFormat) parser.getRecentDateFormat().clone(); // assume we're in the US Pacific Time Zone final TimeZone tzla = TimeZone.getTimeZone("America/Los_Angeles"); sdf.setTimeZone(tzla); // get formatted versions of time in L.A. final String fmtTimePlusOneHour = sdf.format(anHourFromNow); final String fmtTimePlusThreeHours = sdf.format(threeHoursFromNow); try { final Calendar parsed = parser.parseTimestamp(fmtTimePlusOneHour); // the only difference should be the two hours // difference, no rolling back a year should occur. assertEquals("no.rollback.because.of.time.zones", TWO_HOURS_OF_MILLISECONDS, cal.getTime().getTime() - parsed.getTime().getTime()); } catch (final ParseException e) { fail("Unable to parse " + fmtTimePlusOneHour); } // but if the file's timestamp is THREE hours ahead of now, that should // cause a rollover even taking the time zone difference into account. // Since that time is still later than ours, it is parsed as occurring // on this date last year. try { final Calendar parsed = parser.parseTimestamp(fmtTimePlusThreeHours); // rollback should occur here. assertEquals("rollback.even.with.time.zones", 1, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); } catch (final ParseException e) { fail("Unable to parse" + fmtTimePlusThreeHours); } } public void testParseTimestampWithSlop() { final Calendar cal = Calendar.getInstance(); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); final Calendar caltemp = (Calendar) cal.clone(); caltemp.add(Calendar.HOUR_OF_DAY, 1); final Date anHourFromNow = caltemp.getTime(); caltemp.add(Calendar.DAY_OF_MONTH, 1); final Date anHourFromNowTomorrow = caltemp.getTime(); final FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); // set the "slop" factor on parser.setLenientFutureDates(true); final SimpleDateFormat sdf = new SimpleDateFormat(parser.getRecentDateFormatString()); try { String fmtTime = sdf.format(anHourFromNow); Calendar parsed = parser.parseTimestamp(fmtTime); // the timestamp is ahead of now (by one hour), but // that's within range of the "slop" factor. // so the date is still considered this year. assertEquals("test.slop.no.roll.back.year", 0, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); // add a day to get beyond the range of the slop factor. // this must mean the file's date refers to a year ago. fmtTime = sdf.format(anHourFromNowTomorrow); parsed = parser.parseTimestamp(fmtTime); assertEquals("test.slop.roll.back.year", 1, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); } catch (final ParseException e) { fail("Unable to parse"); } } } MLSDComparison.java000066400000000000000000000177531434047722200334240ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.ftp.parser; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.InputStream; import java.util.Arrays; import java.util.Calendar; import java.util.Comparator; import java.util.TimeZone; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileFilters; import org.apache.commons.net.ftp.FTPListParseEngine; import org.junit.Test; /** * Attempt comparison of LIST and MLSD listings * * TODO - needs some work. */ public class MLSDComparison { private final Comparator cmp = (o1, o2) -> { final String n1 = o1.getName(); final String n2 = o2.getName(); return n1.compareTo(n2); }; /** * Compare two instances to see if they are the same, ignoring any uninitialized fields. * * @param a first instance * @param b second instance * @return true if the initialized fields are the same * @since 3.0 */ public boolean areEquivalent(final FTPFile a, final FTPFile b) { return a.getName().equals(b.getName()) && areSame(a.getSize(), b.getSize(), -1L) && // areSame(a.getUser(), b.getUser()) && // areSame(a.getGroup(), b.getGroup()) && areSame(a.getTimestamp(), b.getTimestamp()) && // areSame(a.getType(), b.getType(), UNKNOWN_TYPE) && // areSame(a.getHardLinkCount(), b.getHardLinkCount(), 0) && // areSame(a._permissions, b._permissions) true; } private boolean areSame(final Calendar a, final Calendar b) { return a == null || b == null || areSameDateTime(a, b); } private boolean areSame(final long a, final long b, final long d) { return a == d || b == d || a == b; } // compare permissions: default is all false, but that is also a possible // state, so this may miss some differences // private boolean areSame(boolean[][] a, boolean[][] b) { // return isDefault(a) || isDefault(b) || Arrays.deepEquals(a, b); // } // Is the array in its default state? // private boolean isDefault(boolean[][] a) { // for(boolean[] r : a){ // for(boolean rc : r){ // if (rc) { // not default // return false; // } // } // } // return true; // } private boolean areSameDateTime(final Calendar a, final Calendar b) { final TimeZone UTC = TimeZone.getTimeZone("UTC"); final Calendar ac = Calendar.getInstance(UTC); ac.setTime(a.getTime()); final Calendar bc = Calendar.getInstance(UTC); bc.setTime(b.getTime()); return isSameDay(ac, bc) && isSameTime(ac, bc); } private void compareSortedLists(final FTPFile[] lst, final FTPFile[] mlst) { Arrays.sort(lst, cmp); Arrays.sort(mlst, cmp); FTPFile first, second; final int firstl = lst.length; final int secondl = mlst.length; int one = 0, two = 0; first = lst[one++]; second = mlst[two++]; int cmp; while (one < firstl || two < secondl) { // String fs1 = first.toFormattedString(); // String fs2 = second.toFormattedString(); final String rl1 = first.getRawListing(); final String rl2 = second.getRawListing(); cmp = first.getName().compareTo(second.getName()); if (cmp == 0) { if (first.getName().endsWith("HEADER.html")) { cmp = 0; } if (!areEquivalent(first, second)) { // System.out.println(rl1); // System.out.println(fs1); final long tdiff = first.getTimestamp().getTimeInMillis() - second.getTimestamp().getTimeInMillis(); System.out.println("Minutes diff " + tdiff / (1000 * 60)); // System.out.println(fs2); // System.out.println(rl2); // System.out.println(); // fail(); } if (one < firstl) { first = lst[one++]; } if (two < secondl) { second = mlst[two++]; } } else if (cmp < 0) { if (!first.getName().startsWith(".")) { // skip hidden files System.out.println("1: " + rl1); } if (one < firstl) { first = lst[one++]; } } else { System.out.println("2: " + rl2); if (two < secondl) { second = mlst[two++]; } } } } private boolean isSameDay(final Calendar a, final Calendar b) { final int ad = a.get(Calendar.DAY_OF_MONTH); final int bd = b.get(Calendar.DAY_OF_MONTH); return a.get(Calendar.YEAR) == b.get(Calendar.YEAR) && a.get(Calendar.MONTH) == b.get(Calendar.MONTH) && ad == bd; } private boolean isSameTime(final Calendar a, final Calendar b) { final int ah = a.get(Calendar.HOUR_OF_DAY); final int bh = b.get(Calendar.HOUR_OF_DAY); final int am = a.get(Calendar.MINUTE); final int bm = b.get(Calendar.MINUTE); final int as = a.get(Calendar.SECOND); final int bs = b.get(Calendar.SECOND); return (ah == 0 && am == 0 && as == 0) || (bh == 0 && bm == 0 && bs == 0) || (ah == bh && am == bm) // ignore seconds ; } @Test public void testFile() throws Exception { final File path = new File(DownloadListings.DOWNLOAD_DIR); final FilenameFilter filter = (dir, name) -> name.endsWith("_mlsd.txt"); final File[] files = path.listFiles(filter); if (files != null) { for (final File mlsd : files) { System.out.println(mlsd); FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance()); try (final InputStream is = new FileInputStream(mlsd)) { engine.readServerList(is, FTP.DEFAULT_CONTROL_ENCODING); } final FTPFile[] mlsds = engine.getFiles(FTPFileFilters.ALL); final File listFile = new File(mlsd.getParentFile(), mlsd.getName().replace("_mlsd", "_list")); try (final InputStream inputStream = new FileInputStream(listFile)) { final FTPClientConfig cfg = new FTPClientConfig(); cfg.setServerTimeZoneId("GMT"); final UnixFTPEntryParser parser = new UnixFTPEntryParser(cfg); engine = new FTPListParseEngine(parser); engine.readServerList(inputStream, FTP.DEFAULT_CONTROL_ENCODING); final FTPFile[] lists = engine.getFiles(FTPFileFilters.ALL); compareSortedLists(mlsds, lists); } } } } // private boolean areSame(int a, int b, int d) { // return a == d || b == d || a == b; // } // // private boolean areSame(String a, String b) { // return a.length() == 0 || b.length() == 0 || a.equals(b); // } } MLSxEntryParserTest.java000066400000000000000000000074741434047722200345130ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** */ public class MLSxEntryParserTest extends FTPParseTestFramework { private static final String[] badsamples = { "Type=cdir;Modify=20141022065101;UNIX.mode=0775;/no/space", // no space between facts and name "Type=cdir;Modify=20141022065103;UNIX.mode=0775;", // no name or space "/no/leading/space", "", // empty "Type=cdir;Modify=20141022065102;UNIX.mode=0775; ", // no name "Type=dir;Size; missing =size", "Type=dir missing-semicolon", "Type= missing value and semicolon", " ", // no path "Modify=2014; Short stamp", "Type=pdir;Modify=20141205180002Z; /trailing chars in Modify", "Type=dir;Modify=2014102206510x2.999;UNIX.mode=0775; modify has spurious chars", }; private static final String[] goodsamples = { "Type=cdir;Modify=20141022065102;UNIX.mode=0775; /commons/net", "Type=pdir;Modify=20141205180002;UNIX.mode=0775; /commons", "Type=file;Size=431;Modify=20130303210732;UNIX.mode=0664; HEADER.html", "Type=file;Size=1880;Modify=20130611172748;UNIX.mode=0664; README.html", "Type=file;Size=2364;Modify=20130611170131;UNIX.mode=0664; RELEASE-NOTES.txt", "Type=dir;Modify=20141022065102;UNIX.mode=0775; binaries", "Type=dir;Modify=20141022065102.999;UNIX.mode=0775; source", " /no/facts", // no facts "Type=; /empty/fact", "Size=; /empty/size", " Type=cdir;Modify=20141022065102;UNIX.mode=0775; /leading/space", // leading space before facts => it's // a file name! " ", // pathname of space }; public MLSxEntryParserTest(final String name) { super(name); } @Override protected String[] getBadListing() { return badsamples; } @Override protected String[] getGoodListing() { return goodsamples; } @Override protected FTPFileEntryParser getParser() { return MLSxEntryParser.getInstance(); } /** * Check if FTPFile entry parsing failed; i.e. if entry is null. We override parent check, as a null timestamp is not acceptable for these tests. * * @param f FTPFile entry - may be null * @return null if f is null */ @Override protected FTPFile nullFileOrNullDate(final FTPFile f) { return f; } @Override public void testDefaultPrecision() { testPrecision("Type=dir;Modify=20141022065102;UNIX.mode=0775; source", CalendarUnit.SECOND); } @Override public void testParseFieldsOnDirectory() throws Exception { } @Override public void testParseFieldsOnFile() throws Exception { } @Override public void testRecentPrecision() { // borrow this method to test milliseconds testPrecision("Type=dir;Modify=20141022065102.999;UNIX.mode=0775; source", CalendarUnit.MILLISECOND); } } MVSFTPEntryParserTest.java000066400000000000000000000263131434047722200347000ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** * Test suite addapted to new MVSFTPEntryParser.java. */ public class MVSFTPEntryParserTest extends FTPParseTestFramework { private static final String[] goodsamplesDatasetList = { /* Note, if the string begins with SAVE, the parsed entry is stored in the List saveftpfiles */ // "Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname", "SAVE00 3390 2004/06/23 1 1 FB 128 6144 PS INCOMING.RPTBM023.D061704", "SAVE01 3390 2004/06/23 1 1 FB 128 6144 PO INCOMING.RPTBM024.D061704", "SAVE02 3390 2004/06/23 1 1 FB 128 6144 PO-E INCOMING.RPTBM025.D061704", "PSMLC1 3390 2005/04/04 1 1 VB 27994 27998 PS file3.I", "PSMLB9 3390 2005/04/04 1 1 VB 27994 27998 PS file4.I.BU", "PSMLB6 3390 2005/04/05 1 1 VB 27994 27998 PS file3.I.BU", "PSMLC6 3390 2005/04/05 1 1 VB 27994 27998 PS file6.I", "PSMLB7 3390 2005/04/04 1 1 VB 27994 27998 PS file7.O", "PSMLC6 3390 2005/04/05 1 1 VB 27994 27998 PS file7.O.BU", "FPFS49 3390 2004/06/23 1 1 FB 128 6144 PO-E INCOMING.RPTBM026.D061704", "FPFS41 3390 2004/06/23 1 1 FB 128 6144 PS INCOMING.RPTBM056.D061704", "FPFS25 3390 2004/06/23 1 1 FB 128 6144 PS INCOMING.WTM204.D061704", "PEX26F 3390 2017/07/03 115807 FB 29600 29600 PS INCOMING.FIN.D170630.T160630", "VVVVVV 3390 2020/04/18 1 60 U 32760 32760 PO NAME" }; private static final String[] goodsamplesMemberList = { /* Note, if the string begins with SAVE, the parsed entry is stored in the List saveftpfiles */ "Name VV.MM Created Changed Size Init Mod Id", "SAVE03 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001", "SAVE04 ", // no statistics "TBSHELF1 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001", "TBSHELF2 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001", "TBSHELF3 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001", "TBSHELF4 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001", }; private static final String[] goodsamplesJES1List = { /* no header for JES1 (JES Interface level 1) */ /* Note, if the string begins with SAVE, the parsed entry is stored in the List saveftpfiles */ "IBMUSER1 JOB01906 OUTPUT 3 Spool Files", }; private static final String[] goodsamplesJES2List = { /* JES2 (JES Interface level 2) */ /* Note, if the string begins with SAVE, the parsed entry is stored in the List saveftpfiles */ // "JOBNAME JOBID OWNER STATUS CLASS", "IBMUSER2 JOB01906 IBMUSER OUTPUT A RC=0000 3 spool files", "IBMUSER TSU01830 IBMUSER OUTPUT TSU ABEND=522 3 spool files", }; private static final String[] goodsamplesUnixList = { "total 1234", "-rwxr-xr-x 2 root root 4096 Mar 2 15:13 zxbox", "drwxr-xr-x 2 root root 4096 Aug 24 2001 zxjdbc", }; private static final String[] badsamples = { "MigratedP201.$FTXPBI1.$CF2ITB.$AAB0402.I", "PSMLC133902005/04/041VB2799427998PSfile1.I", "file2.O", }; /** * @see junit.framework.TestCase#TestCase(String) */ public MVSFTPEntryParserTest(final String name) { super(name); } @Override public void doAdditionalGoodTests(final String test, final FTPFile f) { assertNotNull("Could not parse raw listing in " + test, f.getRawListing()); assertNotNull("Could not parse name in " + test, f.getName()); // some tests don't produce any further details } protected List getAllGoodListings() { final List l = new ArrayList<>(); l.add(goodsamplesDatasetList); l.add(goodsamplesMemberList); l.add(goodsamplesJES1List); l.add(goodsamplesJES2List); l.add(goodsamplesUnixList); return l; } /* * (non-Javadoc) * * @see org.apache.commons.net.ftp.parser.CompositeFTPParseTestFramework#getBadListings() */ @Override protected String[] getBadListing() { return badsamples; } /* * (non-Javadoc) * * @see org.apache.commons.net.ftp.parser.CompositeFTPParseTestFramework#getGoodListings() */ @Override protected String[] getGoodListing() { return goodsamplesDatasetList; } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#getParser() */ @Override protected FTPFileEntryParser getParser() { return new MVSFTPEntryParser(); } @Override public void testDefaultPrecision() { // TODO Not sure what dates are parsed } /* * note the testGoodListing has to be the first test invoked, because some FTPFile entries are saved for the later tests * * (non-Javadoc) * * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testGoodListing() */ @Override public void testGoodListing() { final String[] goodsamples = getGoodListing(); final MVSFTPEntryParser parser = new MVSFTPEntryParser(); parser.setType(MVSFTPEntryParser.FILE_LIST_TYPE); parser.setRegex(MVSFTPEntryParser.FILE_LIST_REGEX); for (final String test : goodsamples) { final FTPFile f = parser.parseFTPEntry(test); assertNotNull("Failed to parse " + test, f); doAdditionalGoodTests(test, f); } } public void testJesLevel1Listing() { final MVSFTPEntryParser parser = new MVSFTPEntryParser(); parser.setType(MVSFTPEntryParser.JES_LEVEL_1_LIST_TYPE); parser.setRegex(MVSFTPEntryParser.JES_LEVEL_1_LIST_REGEX); for (final String test : goodsamplesJES1List) { final FTPFile f = parser.parseFTPEntry(test); assertNotNull("Failed to parse " + test, f); doAdditionalGoodTests(test, f); } } public void testJesLevel2Listing() { final MVSFTPEntryParser parser = new MVSFTPEntryParser(); parser.setType(MVSFTPEntryParser.JES_LEVEL_2_LIST_TYPE); parser.setRegex(MVSFTPEntryParser.JES_LEVEL_2_LIST_REGEX); for (final String test : goodsamplesJES2List) { final FTPFile f = parser.parseFTPEntry(test); assertNotNull("Failed to parse " + test, f); doAdditionalGoodTests(test, f); } } public void testMemberListing() { final MVSFTPEntryParser parser = new MVSFTPEntryParser(); parser.setType(MVSFTPEntryParser.MEMBER_LIST_TYPE); parser.setRegex(MVSFTPEntryParser.MEMBER_LIST_REGEX); for (final String test : goodsamplesMemberList) { final FTPFile f = parser.parseFTPEntry(test); assertNotNull("Failed to parse " + test, f); doAdditionalGoodTests(test, f); } } @Override public void testParseFieldsOnDirectory() throws Exception { final MVSFTPEntryParser parser = new MVSFTPEntryParser(); parser.setType(MVSFTPEntryParser.FILE_LIST_TYPE); parser.setRegex(MVSFTPEntryParser.FILE_LIST_REGEX); FTPFile file = parser.parseFTPEntry("SAVE01 3390 2004/06/23 1 1 FB 128 6144 PO INCOMING.RPTBM024.D061704"); assertNotNull("Could not parse entry.", file); assertTrue("Should have been a directory.", file.isDirectory()); assertEquals("INCOMING.RPTBM024.D061704", file.getName()); file = parser.parseFTPEntry("SAVE02 3390 2004/06/23 1 1 FB 128 6144 PO-E INCOMING.RPTBM025.D061704"); assertNotNull("Could not parse entry.", file); assertTrue("Should have been a directory.", file.isDirectory()); assertEquals("INCOMING.RPTBM025.D061704", file.getName()); } /* * (non-Javadoc) * * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testParseFieldsOnFile() */ @Override public void testParseFieldsOnFile() throws Exception { FTPFile file; final MVSFTPEntryParser parser = new MVSFTPEntryParser(); parser.setRegex(MVSFTPEntryParser.FILE_LIST_REGEX); parser.setType(MVSFTPEntryParser.FILE_LIST_TYPE); file = parser.parseFTPEntry("SAVE00 3390 2004/06/23 1 1 FB 128 6144 PS INCOMING.RPTBM023.D061704"); assertNotNull("Could not parse entry.", file); assertTrue("Should have been a file.", file.isFile()); assertEquals("INCOMING.RPTBM023.D061704", file.getName()); assertNull("Timestamp should not have been set.", file.getTimestamp()); parser.setType(MVSFTPEntryParser.MEMBER_LIST_TYPE); parser.setRegex(MVSFTPEntryParser.MEMBER_LIST_REGEX); file = parser.parseFTPEntry("SAVE03 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001"); assertNotNull("Could not parse entry.", file); assertTrue("Should have been a file.", file.isFile()); assertEquals("SAVE03", file.getName()); assertNotNull("Timestamp should have been set.", file.getTimestamp()); file = parser.parseFTPEntry("SAVE04 "); assertNotNull("Could not parse entry.", file); assertTrue("Should have been a file.", file.isFile()); assertEquals("SAVE04", file.getName()); assertNull("Timestamp should not have been set.", file.getTimestamp()); } @Override public void testRecentPrecision() { // TODO Auto-generated method stub } public void testUnixListings() { final MVSFTPEntryParser parser = new MVSFTPEntryParser(); final List list = new ArrayList<>(); Collections.addAll(list, goodsamplesUnixList); parser.preParse(list); for (final String test : list) { final FTPFile f = parser.parseFTPEntry(test); assertNotNull("Failed to parse " + test, f); assertNotNull("Failed to parse name " + test, f.getName()); assertNotNull("Failed to parse group " + test, f.getGroup()); assertNotNull("Failed to parse user " + test, f.getUser()); } } } MacOsPeterFTPEntryParserTest.java000066400000000000000000000146371434047722200362430ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.Calendar; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; public class MacOsPeterFTPEntryParserTest extends FTPParseTestFramework { private static final String[] badsamples = { "drwxr-xr-x 123 folder 0 Jan 4 14:49 Steak", }; private static final String[] goodsamples = { "-rw-r--r-- 54149 27826 81975 Jul 22 2010 09.jpg", "drwxr-xr-x folder 0 Jan 4 14:51 Alias_to_Steak", "-rw-r--r-- 78440 49231 127671 Jul 22 2010 Filename with whitespace.jpg", "-rw-r--r-- 78440 49231 127671 Jul 22 14:51 Filename with whitespace.jpg", "-rw-r--r-- 0 108767 108767 Jul 22 2010 presentation03.jpg", "-rw-r--r-- 58679 60393 119072 Jul 22 2010 presentation04.jpg", "-rw-r--r-- 82543 51433 133976 Jul 22 2010 presentation06.jpg", "-rw-r--r-- 83616 1430976 1514592 Jul 22 2010 presentation10.jpg", "-rw-r--r-- 0 66990 66990 Jul 22 2010 presentation11.jpg", "drwxr-xr-x folder 0 Jan 4 14:49 Steak", "-rwx------ 0 12713 12713 Jul 8 2009 Twitter_Avatar.png", }; public MacOsPeterFTPEntryParserTest(final String name) { super(name); } /** * Method checkPermissions. Verify that the persmissions were properly set. * * @param f */ private void checkPermissions(final FTPFile f) { assertTrue("Should have user read permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)); assertTrue("Should have user write permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have user execute permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertTrue("Should have group read permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Should NOT have group write permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have group execute permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertTrue("Should have world read permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Should NOT have world write permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have world execute permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)); } @Override protected String[] getBadListing() { return badsamples; } @Override protected String[] getGoodListing() { return goodsamples; } @Override protected FTPFileEntryParser getParser() { return new MacOsPeterFTPEntryParser(); } @Override public void testDefaultPrecision() { testPrecision("-rw-r--r-- 78440 49231 127671 Jul 22 2010 Filename with whitespace.jpg", CalendarUnit.DAY_OF_MONTH); } @Override public void testParseFieldsOnDirectory() throws Exception { final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x folder 0 Mar 2 15:13 Alias_to_Steak"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a directory.", f.isDirectory()); checkPermissions(f); assertEquals(0, f.getHardLinkCount()); assertNull(f.getUser()); assertNull(f.getGroup()); assertEquals(0, f.getSize()); assertEquals("Alias_to_Steak", f.getName()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); if (f.getTimestamp().getTime().before(cal.getTime())) { cal.add(Calendar.YEAR, -1); } cal.set(Calendar.DAY_OF_MONTH, 2); cal.set(Calendar.HOUR_OF_DAY, 15); cal.set(Calendar.MINUTE, 13); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } @Override public void testParseFieldsOnFile() throws Exception { final FTPFile f = getParser().parseFTPEntry("-rwxr-xr-x 78440 49231 127671 Jul 2 14:51 Filename with whitespace.jpg"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a file.", f.isFile()); checkPermissions(f); assertEquals(0, f.getHardLinkCount()); assertNull(f.getUser()); assertNull(f.getGroup()); assertEquals("Filename with whitespace.jpg", f.getName()); assertEquals(127671L, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.JULY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); if (f.getTimestamp().getTime().before(cal.getTime())) { cal.add(Calendar.YEAR, -1); } cal.set(Calendar.DAY_OF_MONTH, 2); cal.set(Calendar.HOUR_OF_DAY, 14); cal.set(Calendar.MINUTE, 51); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } @Override public void testRecentPrecision() { testPrecision("-rw-r--r-- 78440 49231 127671 Jul 22 14:51 Filename with whitespace.jpg", CalendarUnit.MINUTE); } } NTFTPEntryParserTest.java000066400000000000000000000362421434047722200345560ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.io.ByteArrayInputStream; import java.util.Calendar; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; import org.apache.commons.net.ftp.FTPListParseEngine; /** */ public class NTFTPEntryParserTest extends CompositeFTPParseTestFramework { private static final String[][] goodsamples = { { // DOS-style tests "05-26-95 10:57AM 143712 $LDR$", "05-20-97 03:31PM 681 .bash_history", "12-05-96 05:03PM absoft2", "11-14-97 04:21PM 953 AUDITOR3.INI", "05-22-97 08:08AM 828 AUTOEXEC.BAK", "01-22-98 01:52PM 795 AUTOEXEC.BAT", "05-13-97 01:46PM 828 AUTOEXEC.DOS", "12-03-96 06:38AM 403 AUTOTOOL.LOG", "12-03-96 06:38AM 123xyz", "01-20-97 03:48PM bin", "05-26-1995 10:57AM 143712 $LDR$", // 24hr clock as used on Windows_CE "12-05-96 17:03 absoft2", "05-22-97 08:08 828 AUTOEXEC.BAK", "01-01-98 05:00 Network", "01-01-98 05:00 StorageCard", "09-13-10 20:08 Recycled", "09-06-06 19:00 69 desktop.ini", "09-13-10 13:08 23 Control Panel.lnk", "09-13-10 13:08 My Documents", "09-13-10 13:08 Program Files", "09-13-10 13:08 Temp", "09-13-10 13:08 Windows", }, { // Unix-style tests "-rw-r--r-- 1 root root 111325 Apr 27 2001 zxJDBC-2.0.1b1.tar.gz", "-rw-r--r-- 1 root root 190144 Apr 27 2001 zxJDBC-2.0.1b1.zip", "-rwxr-xr-x 2 500 500 166 Nov 2 2001 73131-testtes1.afp", "-rw-r--r-- 1 500 500 166 Nov 9 2001 73131-testtes1.AFP", "drwx------ 4 maxm Domain Users 512 Oct 2 10:59 .metadata", } }; private static final String[][] badsamples = { { // DOS-style tests "20-05-97 03:31PM 681 .bash_history", " 0 DIR 05-19-97 12:56 local", " 0 DIR 05-12-97 16:52 Maintenance Desktop", }, { // Unix-style tests "drwxr-xr-x 2 root 99 4096Feb 23 30:01 zzplayer", } }; private static final String directoryBeginningWithNumber = "12-03-96 06:38AM 123xyz"; // byte -123 when read using ISO-8859-1 encoding becomes 0X85 line terminator private static final byte[] listFilesByteTrace = { 48, 57, 45, 48, 52, 45, 49, 51, 32, 32, 48, 53, 58, 53, 49, 80, 77, 32, 32, 32, 32, 32, 32, 32, 60, 68, 73, 82, 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 97, 115, 112, 110, 101, 116, 95, 99, 108, 105, 101, 110, 116, 13, 10, // 1 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 53, 52, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 50, 32, 65, 95, 113, 117, 105, 99, 107, 95, 98, 114, 111, 119, 110, 95, 102, 111, 120, 95, 106, 117, 109, 112, 115, 95, 111, 118, 101, 114, 95, 116, 104, 101, 95, 108, 97, 122, 121, 95, 100, 111, 103, 13, 10, // 2 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 49, 55, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 51, 32, 120, -127, -123, 121, 13, 10, // 3 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 49, 58, 52, 57, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 52, 32, -126, -28, -126, -83, -119, -51, -126, -52, -105, -84, -126, -22, -126, -51, -112, -30, -126, -90, -126, -72, -126, -75, -126, -60, -127, 65, -126, -75, -126, -87, -126, -32, -126, -32, -126, -58, -126, -52, -112, -123, -126, -55, -126, -96, -126, -25, -126, -72, 46, 116, 120, 116, 13, 10, // 4 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 52, 54, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 53, 32, -125, 76, -125, -125, -125, 98, -125, 86, -125, 116, -125, -115, -127, 91, -116, 118, -114, 90, -113, -111, 13, 10, // 5 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 52, 54, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 54, 32, -125, 76, -125, -125, -125, 98, -125, 86, -125, -123, -125, 116, -125, -115, -127, 91, -116, 118, -114, 90, -113, -111, 13, 10, // 6 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 49, 58, 52, 57, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 55, 32, -114, 79, -116, -38, -126, -52, -105, -25, 46, 116, 120, 116, 13, 10, // 7 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 49, 58, 52, 57, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 56, 32, -111, -66, -116, -10, -106, 93, 46, 116, 120, 116, 13, 10, // 8 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 53, 52, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 57, 32, -113, -84, -106, -20, -106, -123, -114, 113, 13, 10, // 9 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 49, 58, 52, 57, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 48, 32, -119, -28, -109, 99, -118, -108, -114, -82, -119, -17, -114, -48, -120, -8, -112, -123, -108, 95, -117, -58, 46, 80, 68, 70, 13, 10, // 10 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 49, 49, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 49, 32, -112, -124, -99, -56, 46, 116, 120, 116, 13, 10, // 11 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 52, 51, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 50, 32, -117, -76, -116, -123, 13, 10, // 12 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 49, 50, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 51, 32, -114, -107, -111, -123, -108, 94, -104, 82, 13, 10, // 13 48, 55, 45, 48, 51, 45, 49, 51, 32, 32, 48, 50, 58, 51, 53, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 52, 32, -112, -123, -117, -101, -126, -52, -116, -16, -126, -19, -126, -24, 46, 116, 120, 116, 13, 10, // 14 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 50, 58, 49, 50, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 53, 32, -114, -123, -117, -101, -112, -20, 13, 10, // 15 48, 55, 45, 49, 55, 45, 49, 51, 32, 32, 48, 49, 58, 52, 57, 80, 77, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 54, 32, -107, -94, -112, -123, -106, 126, -126, -55, -107, -44, -126, -25, -126, -72, 46, 116, 120, 116, 13, 10 // 16 }; private static final int LISTFILE_COUNT = 16; /** * @see junit.framework.TestCase#TestCase(String) */ public NTFTPEntryParserTest(final String name) { super(name); } @Override protected void doAdditionalGoodTests(final String test, final FTPFile f) { if (test.indexOf("") >= 0) { assertEquals("directory.type", FTPFile.DIRECTORY_TYPE, f.getType()); } } /** * @see org.apache.commons.net.ftp.parser.CompositeFTPParseTestFramework#getBadListings() */ @Override protected String[][] getBadListings() { return badsamples; } /** * @see org.apache.commons.net.ftp.parser.CompositeFTPParseTestFramework#getGoodListings() */ @Override protected String[][] getGoodListings() { return goodsamples; } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#getParser() */ @Override protected FTPFileEntryParser getParser() { return new CompositeFileEntryParser(new FTPFileEntryParser[] { new NTFTPEntryParser(), new UnixFTPEntryParser() }); } @Override public void testDefaultPrecision() { testPrecision("05-26-1995 10:57AM 143712 $LDR$", CalendarUnit.MINUTE); testPrecision("05-22-97 08:08 828 AUTOEXEC.BAK", CalendarUnit.MINUTE); } /* * test condition reported as bug 20259 - now NET-106. directory with name beginning with a numeric character was not parsing correctly */ public void testDirectoryBeginningWithNumber() { final FTPFile f = getParser().parseFTPEntry(directoryBeginningWithNumber); assertEquals("name", "123xyz", f.getName()); } public void testDirectoryBeginningWithNumberFollowedBySpaces() { FTPFile f = getParser().parseFTPEntry("12-03-96 06:38AM 123 xyz"); assertEquals("name", "123 xyz", f.getName()); f = getParser().parseFTPEntry("12-03-96 06:38AM 123 abc xyz"); assertNotNull(f); assertEquals("name", "123 abc xyz", f.getName()); } /* * Test that group names with embedded spaces can be handled correctly * */ public void testGroupNameWithSpaces() { final FTPFile f = getParser().parseFTPEntry("drwx------ 4 maxm Domain Users 512 Oct 2 10:59 .metadata"); assertNotNull(f); assertEquals("maxm", f.getUser()); assertEquals("Domain Users", f.getGroup()); } public void testNET339() { final FTPFile file = getParser().parseFTPEntry("05-22-97 12:08 5000000000 10 years and under"); assertNotNull("Could not parse entry", file); assertEquals("10 years and under", file.getName()); assertEquals(5000000000L, file.getSize()); Calendar timestamp = file.getTimestamp(); assertNotNull("Could not parse time", timestamp); assertEquals("Thu May 22 12:08:00 1997", df.format(timestamp.getTime())); final FTPFile dir = getParser().parseFTPEntry("12-03-96 06:38 10 years and under"); assertNotNull("Could not parse entry", dir); assertEquals("10 years and under", dir.getName()); timestamp = dir.getTimestamp(); assertNotNull("Could not parse time", timestamp); assertEquals("Tue Dec 03 06:38:00 1996", df.format(timestamp.getTime())); } public void testNET516() throws Exception { // problem where part of a multi-byte char gets converted to 0x85 = line term final int utf = testNET516("UTF-8"); assertEquals(LISTFILE_COUNT, utf); final int ascii = testNET516("ASCII"); assertEquals(LISTFILE_COUNT, ascii); final int iso8859_1 = testNET516("ISO-8859-1"); assertEquals(LISTFILE_COUNT, iso8859_1); } private int testNET516(final String charset) throws Exception { final FTPFileEntryParser parser = new NTFTPEntryParser(); final FTPListParseEngine engine = new FTPListParseEngine(parser); engine.readServerList(new ByteArrayInputStream(listFilesByteTrace), charset); final FTPFile[] ftpfiles = engine.getFiles(); return ftpfiles.length; } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testParseFieldsOnDirectory() */ @Override public void testParseFieldsOnDirectory() throws Exception { FTPFile dir = getParser().parseFTPEntry("12-05-96 05:03PM absoft2"); assertNotNull("Could not parse entry.", dir); assertEquals("Thu Dec 05 17:03:00 1996", df.format(dir.getTimestamp().getTime())); assertTrue("Should have been a directory.", dir.isDirectory()); assertEquals("absoft2", dir.getName()); assertEquals(0, dir.getSize()); dir = getParser().parseFTPEntry("12-03-96 06:38AM 123456"); assertNotNull("Could not parse entry.", dir); assertTrue("Should have been a directory.", dir.isDirectory()); assertEquals("123456", dir.getName()); assertEquals(0, dir.getSize()); } /** * @see org.apache.commons.net.ftp.parser.FTPParseTestFramework#testParseFieldsOnFile() */ @Override public void testParseFieldsOnFile() throws Exception { FTPFile f = getParser().parseFTPEntry("05-22-97 12:08AM 5000000000 AUTOEXEC.BAK"); assertNotNull("Could not parse entry.", f); assertEquals("Thu May 22 00:08:00 1997", df.format(f.getTimestamp().getTime())); assertTrue("Should have been a file.", f.isFile()); assertEquals("AUTOEXEC.BAK", f.getName()); assertEquals(5000000000L, f.getSize()); // test an NT-unix style listing that does NOT have a leading zero // on the hour. f = getParser().parseFTPEntry("-rw-rw-r-- 1 mqm mqm 17707 Mar 12 3:33 killmq.sh.log"); assertNotNull("Could not parse entry.", f); final Calendar cal = Calendar.getInstance(); cal.setTime(f.getTimestamp().getTime()); assertEquals("hour", 3, cal.get(Calendar.HOUR)); assertTrue("Should have been a file.", f.isFile()); assertEquals(17707, f.getSize()); } public void testParseLeadingDigits() { final FTPFile file = getParser().parseFTPEntry("05-22-97 12:08AM 5000000000 10 years and under"); assertNotNull("Could not parse entry", file); assertEquals("10 years and under", file.getName()); assertEquals(5000000000L, file.getSize()); Calendar timestamp = file.getTimestamp(); assertNotNull("Could not parse time", timestamp); assertEquals("Thu May 22 00:08:00 1997", df.format(timestamp.getTime())); final FTPFile dir = getParser().parseFTPEntry("12-03-96 06:38PM 10 years and under"); assertNotNull("Could not parse entry", dir); assertEquals("10 years and under", dir.getName()); timestamp = dir.getTimestamp(); assertNotNull("Could not parse time", timestamp); assertEquals("Tue Dec 03 18:38:00 1996", df.format(timestamp.getTime())); } @Override public void testRecentPrecision() { // Not used } } NetwareFTPEntryParserTest.java000066400000000000000000000100661434047722200356360ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.Calendar; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** */ public class NetwareFTPEntryParserTest extends FTPParseTestFramework { private static final String[] badsamples = { "a [-----F--] SCION_SYS 512 Apr 13 23:52 SYS", "d [----AF--] 0 512 10-04-2001 _ADMIN" }; private static final String[] goodsamples = { "d [-----F--] SCION_SYS 512 Apr 13 23:52 SYS", "d [----AF--] 0 512 Feb 22 17:32 _ADMIN", "d [-W---F--] SCION_VOL2 512 Apr 13 23:12 VOL2", "- [RWCEAFMS] rwinston 19968 Mar 12 15:20 Executive Summary.doc", "d [RWCEAFMS] rwinston 512 Nov 24 2005 Favorites" }; public NetwareFTPEntryParserTest(final String name) { super(name); } @Override protected String[] getBadListing() { return badsamples; } @Override protected String[] getGoodListing() { return goodsamples; } @Override protected FTPFileEntryParser getParser() { return new NetwareFTPEntryParser(); } @Override public void testDefaultPrecision() { testPrecision("d [RWCEAFMS] rwinston 512 Nov 24 2005 Favorites", CalendarUnit.DAY_OF_MONTH); } @Override public void testParseFieldsOnDirectory() throws Exception { final String reply = "d [-W---F--] testUser 512 Apr 13 23:12 testFile"; final FTPFile f = getParser().parseFTPEntry(reply); assertNotNull("Could not parse file", f); assertEquals("testFile", f.getName()); assertEquals(512L, f.getSize()); assertEquals("testUser", f.getUser()); assertTrue("Directory flag is not set!", f.isDirectory()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, 3); cal.set(Calendar.DAY_OF_MONTH, 13); cal.set(Calendar.HOUR_OF_DAY, 23); cal.set(Calendar.MINUTE, 12); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); cal.set(Calendar.YEAR, f.getTimestamp().get(Calendar.YEAR)); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } @Override public void testParseFieldsOnFile() throws Exception { final String reply = "- [R-CEAFMS] rwinston 19968 Mar 12 15:20 Document name with spaces.doc"; final FTPFile f = getParser().parseFTPEntry(reply); assertNotNull("Could not parse file", f); assertEquals("Document name with spaces.doc", f.getName()); assertEquals(19968L, f.getSize()); assertEquals("rwinston", f.getUser()); assertTrue("File flag is not set!", f.isFile()); assertTrue(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)); assertFalse(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)); } @Override public void testRecentPrecision() { testPrecision("- [RWCEAFMS] rwinston 19968 Mar 12 15:20 Executive Summary.doc", CalendarUnit.MINUTE); } } OS2FTPEntryParserTest.java000066400000000000000000000077331434047722200346430ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; public class OS2FTPEntryParserTest extends FTPParseTestFramework { private static final String[] badsamples = { " DIR 12-30-97 12:32 jbrekke", " 0 rsa DIR 11-25-97 09:42 junk", " 0 dir 05-12-97 16:44 LANGUAGE", " 0 DIR 13-05-97 25:49 MPTN", "587823 RSA DIR Jan-08-97 13:58 OS2KRNL", " 33280 A 1997-02-03 13:49 OS2LDR", "12-05-96 05:03PM absoft2", "11-14-97 04:21PM 953 AUDITOR3.INI" }; private static final String[] goodsamples = { " 0 DIR 12-30-97 12:32 jbrekke", " 0 DIR 11-25-97 09:42 junk", " 0 DIR 05-12-97 16:44 LANGUAGE", " 0 DIR 05-19-97 12:56 local", " 0 DIR 05-12-97 16:52 Maintenance Desktop", " 0 DIR 05-13-97 10:49 MPTN", "587823 RSA DIR 01-08-97 13:58 OS2KRNL", " 33280 A 02-09-97 13:49 OS2LDR", " 0 DIR 11-28-97 09:42 PC", "149473 A 11-17-98 16:07 POPUPLOG.OS2", " 0 DIR 05-12-97 16:44 PSFONTS", " 0 DIR 05-19-2000 12:56 local", }; public OS2FTPEntryParserTest(final String name) { super(name); } @Override protected String[] getBadListing() { return badsamples; } @Override protected String[] getGoodListing() { return goodsamples; } @Override protected FTPFileEntryParser getParser() { final ConfigurableFTPFileEntryParserImpl parser = new OS2FTPEntryParser(); parser.configure(null); return parser; } @Override public void testDefaultPrecision() { testPrecision(" 0 DIR 05-12-97 16:44 PSFONTS", CalendarUnit.MINUTE); testPrecision(" 0 DIR 05-19-2000 12:56 local", CalendarUnit.MINUTE); } @Override public void testParseFieldsOnDirectory() throws Exception { final FTPFile dir = getParser().parseFTPEntry(" 0 DIR 11-28-97 09:42 PC"); assertNotNull("Could not parse entry.", dir); assertTrue("Should have been a directory.", dir.isDirectory()); assertEquals(0, dir.getSize()); assertEquals("PC", dir.getName()); assertEquals("Fri Nov 28 09:42:00 1997", df.format(dir.getTimestamp().getTime())); } @Override public void testParseFieldsOnFile() throws Exception { final FTPFile file = getParser().parseFTPEntry("5000000000 A 11-17-98 16:07 POPUPLOG.OS2"); assertNotNull("Could not parse entry.", file); assertTrue("Should have been a file.", file.isFile()); assertEquals(5000000000L, file.getSize()); assertEquals("POPUPLOG.OS2", file.getName()); assertEquals("Tue Nov 17 16:07:00 1998", df.format(file.getTimestamp().getTime())); } @Override public void testRecentPrecision() { // Not needed } } OS400FTPEntryParserAdditionalTest.java000066400000000000000000000111751434047722200367710ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.Calendar; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** */ public class OS400FTPEntryParserAdditionalTest extends CompositeFTPParseTestFramework { private static final String[][] badsamples = { { "QPGMR 135168 04/03/18 13:18:19 *FILE", "QPGMR 135168 03/24 13:18:19 *FILE", "QPGMR 135168 04/03/18 30:06:29 *FILE", "QPGMR 04/03/18 13:18:19 *FILE RPGUNITC1.FILE", "QPGMR 135168 03/24 13:18:19 *FILE RPGUNITC1.FILE", "QPGMR 135168 04/03/18 30:06:29 *FILE RPGUNITC1.FILE", "QPGMR *MEM ", "QPGMR 135168 04/03/18 13:18:19 *MEM RPGUNITC1.FILE/RUCALLTST.MBR", "QPGMR 135168 *MEM RPGUNITC1.FILE/RUCALLTST.MBR", "QPGMR 04/03/18 13:18:19 *MEM RPGUNITC1.FILE/RUCALLTST.MBR", "QPGMR USR *MEM RPGUNITC1.FILE/RUCALLTST.MBR" } }; private static final String[][] goodsamples = { { "QPGMR *MEM RPGUNITC1.FILE/RUCALLTST.MBR", "QPGMR 16347136 29.06.13 15:45:09 *FILE RPGUNIT.SAVF" } }; public OS400FTPEntryParserAdditionalTest(final String name) { super(name); } @Override protected void doAdditionalGoodTests(final String test, final FTPFile f) { if (test.startsWith("d")) { assertEquals("directory.type", FTPFile.DIRECTORY_TYPE, f.getType()); } } @Override protected String[][] getBadListings() { return badsamples; } @Override protected String[][] getGoodListings() { return goodsamples; } @Override protected FTPFileEntryParser getParser() { return new CompositeFileEntryParser(new FTPFileEntryParser[] { new OS400FTPEntryParser(), new UnixFTPEntryParser() }); } @Override public void testDefaultPrecision() { // Done in other class } @Override public void testParseFieldsOnDirectory() throws Exception { final FTPFile f = getParser().parseFTPEntry("PEP 36864 04/03/24 14:06:34 *DIR dir1/"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a directory.", f.isDirectory()); assertEquals("PEP", f.getUser()); assertEquals("dir1", f.getName()); assertEquals(36864, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.YEAR, 2004); cal.set(Calendar.DAY_OF_MONTH, 24); cal.set(Calendar.HOUR_OF_DAY, 14); cal.set(Calendar.MINUTE, 6); cal.set(Calendar.SECOND, 34); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } @Override public void testParseFieldsOnFile() throws Exception { final FTPFile f = getParser().parseFTPEntry("PEP 5000000000 04/03/24 14:06:29 *STMF build.xml"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a file.", f.isFile()); assertEquals("PEP", f.getUser()); assertEquals("build.xml", f.getName()); assertEquals(5000000000L, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.DAY_OF_MONTH, 24); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.YEAR, 2004); cal.set(Calendar.HOUR_OF_DAY, 14); cal.set(Calendar.MINUTE, 6); cal.set(Calendar.SECOND, 29); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } @Override public void testRecentPrecision() { // Done in other class } } OS400FTPEntryParserTest.java000066400000000000000000000162311434047722200347760ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.Calendar; import org.apache.commons.net.ftp.FTPClientConfig; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** */ public class OS400FTPEntryParserTest extends CompositeFTPParseTestFramework { private static final String[][] badsamples = { { "PEP 4019 04/03/18 18:58:16 STMF einladung.zip", "PEP 422 03/24 14:06:26 *STMF readme", "PEP 6409 04/03/24 30:06:29 *STMF build.xml", "PEP USR 36864 04/03/24 14:06:34 *DIR dir1/", "PEP 3686404/03/24 14:06:47 *DIR zdir2/" }, { "----rwxr-x 1PEP 0 4019 Mar 18 18:58 einladung.zip", "----rwxr-x 1 PEP 0 xx 422 Mar 24 14:06 readme", "----rwxr-x 1 PEP 0 8492 Apr 07 30:13 build.xml", "d---rwxr-x 2 PEP 0 45056Mar 24 14:06 zdir2" } }; private static final String[][] goodsamples = { { "PEP 4019 04/03/18 18:58:16 *STMF einladung.zip", "PEP 422 04/03/24 14:06:26 *STMF readme", "PEP 6409 04/03/24 14:06:29 *STMF build.xml", "PEP 36864 04/03/24 14:06:34 *DIR dir1/", "PEP 36864 04/03/24 14:06:47 *DIR zdir2/" }, { "----rwxr-x 1 PEP 0 4019 Mar 18 18:58 einladung.zip", "----rwxr-x 1 PEP 0 422 Mar 24 14:06 readme", "----rwxr-x 1 PEP 0 8492 Apr 07 07:13 build.xml", "d---rwxr-x 2 PEP 0 45056 Mar 24 14:06 dir1", "d---rwxr-x 2 PEP 0 45056 Mar 24 14:06 zdir2" } }; /** * @see junit.framework.TestCase#TestCase(String) */ public OS400FTPEntryParserTest(final String name) { super(name); } @Override protected void doAdditionalGoodTests(final String test, final FTPFile f) { if (test.startsWith("d")) { assertEquals("directory.type", FTPFile.DIRECTORY_TYPE, f.getType()); } } /** * @see FTPParseTestFramework#getBadListing() */ @Override protected String[][] getBadListings() { return badsamples; } /** * @see FTPParseTestFramework#getGoodListing() */ @Override protected String[][] getGoodListings() { return goodsamples; } /** * @see FTPParseTestFramework#getParser() */ @Override protected FTPFileEntryParser getParser() { return new CompositeFileEntryParser(new FTPFileEntryParser[] { new OS400FTPEntryParser(), new UnixFTPEntryParser() }); } @Override public void testDefaultPrecision() { testPrecision("PEP 4019 04/03/18 18:58:16 *STMF einladung.zip", CalendarUnit.SECOND); } public void testNET573() { final FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_AS400); conf.setDefaultDateFormatStr("MM/dd/yy HH:mm:ss"); final FTPFileEntryParser parser = new OS400FTPEntryParser(conf); final FTPFile f = parser.parseFTPEntry("ZFTPDEV 9069 05/20/15 15:36:52 *STMF /DRV/AUDWRKSHET/AUDWRK0204232015114625.PDF"); assertNotNull("Could not parse entry.", f); assertNotNull("Could not parse timestamp.", f.getTimestamp()); assertFalse("Should not have been a directory.", f.isDirectory()); assertEquals("ZFTPDEV", f.getUser()); assertEquals("AUDWRK0204232015114625.PDF", f.getName()); assertEquals(9069, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, 2015); cal.set(Calendar.MONTH, Calendar.MAY); cal.set(Calendar.DAY_OF_MONTH, 20); cal.set(Calendar.HOUR_OF_DAY, 15); cal.set(Calendar.MINUTE, 36); cal.set(Calendar.SECOND, 52); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } /** * @see FTPParseTestFramework#testParseFieldsOnDirectory() */ @Override public void testParseFieldsOnDirectory() throws Exception { final FTPFile f = getParser().parseFTPEntry("PEP 36864 04/03/24 14:06:34 *DIR dir1/"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a directory.", f.isDirectory()); assertEquals("PEP", f.getUser()); assertEquals("dir1", f.getName()); assertEquals(36864, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.YEAR, 2004); cal.set(Calendar.DAY_OF_MONTH, 24); cal.set(Calendar.HOUR_OF_DAY, 14); cal.set(Calendar.MINUTE, 6); cal.set(Calendar.SECOND, 34); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } /** * @see FTPParseTestFramework#testParseFieldsOnFile() */ @Override public void testParseFieldsOnFile() throws Exception { final FTPFile f = getParser().parseFTPEntry("PEP 5000000000 04/03/24 14:06:29 *STMF build.xml"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a file.", f.isFile()); assertEquals("PEP", f.getUser()); assertEquals("build.xml", f.getName()); assertEquals(5000000000L, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.DAY_OF_MONTH, 24); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.YEAR, 2004); cal.set(Calendar.HOUR_OF_DAY, 14); cal.set(Calendar.MINUTE, 6); cal.set(Calendar.SECOND, 29); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } /** * Test file names with spaces. */ public void testParseFileNameWithSpaces() { final FTPFile f = getParser().parseFTPEntry("MYUSER 3 06/12/21 12:00:00 *STMF file with space.txt"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a file.", f.isFile()); assertEquals("file with space.txt", f.getName()); } @Override public void testRecentPrecision() { testPrecision("----rwxr-x 1 PEP 0 4019 Mar 18 18:58 einladung.zip", CalendarUnit.MINUTE); } } UnixFTPEntryParserTest.java000066400000000000000000000462311434047722200351570ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.util.Calendar; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; /** */ public class UnixFTPEntryParserTest extends FTPParseTestFramework { private static final String[] badsamples = { "zrwxr-xr-x 2 root root 4096 Mar 2 15:13 zxbox", "dxrwr-xr-x 2 root root 4096 Aug 24 2001 zxjdbc", "drwxr-xr-x 2 root root 4096 Jam 4 00:03 zziplib", "drwxr-xr-x 2 root 99 4096 Feb 23 30:01 zzplayer", "drwxr-xr-x 2 root root 4096 Aug 36 2001 zztpp", "-rw-r--r-- 1 14 staff 80284 Aug 22 zxJDBC-1.2.3.tar.gz", "-rw-r--r-- 1 14 staff 119:26 Aug 22 2000 zxJDBC-1.2.3.zip", /* "-rw-r--r-- 1 ftp no group 83853 Jan 22 2001 zxJDBC-1.2.4.tar.gz", */ "-rw-r--r-- 1ftp nogroup 126552 Jan 22 2001 zxJDBC-1.2.4.zip", "-rw-r--r-- 1 root root 190144 2001-04-27 zxJDBC-2.0.1b1.zip", "-rw-r--r-- 1 root root 111325 Apr -7 18:79 zxJDBC-2.0.1b1.tar.gz" }; private static final String[] goodsamples = { "-rw-r--r-- 1 500 500 21 Aug 8 14:14 JB3-TES1.gz", "-rwxr-xr-x 2 root root 4096 Mar 2 15:13 zxbox", "drwxr-xr-x 2 root root 4096 Aug 24 2001 zxjdbc", "drwxr-xr-x 2 root root 4096 Jan 4 00:03 zziplib", "drwxr-xr-x 2 root 99 4096 Feb 23 2001 zzplayer", "drwxr-xr-x 2 root root 4096 Aug 6 2001 zztpp", "drwxr-xr-x 1 usernameftp 512 Jan 29 23:32 prog", "lrw-r--r-- 1 14 14 80284 Aug 22 2000 zxJDBC-1.2.3.tar.gz", "frw-r--r-- 1 14 staff 119926 Aug 22 2000 zxJDBC-1.2.3.zip", "crw-r--r-- 1 ftp nogroup 83853 Jan 22 2001 zxJDBC-1.2.4.tar.gz", "brw-r--r-- 1 ftp nogroup 126552 Jan 22 2001 zxJDBC-1.2.4.zip", "-rw-r--r-- 1 root root 111325 Apr 27 2001 zxJDBC-2.0.1b1.tar.gz", "-rw-r--r-- 1 root root 190144 Apr 27 2001 zxJDBC-2.0.1b1.zip", "-rwxr-xr-x 2 500 500 166 Nov 2 2001 73131-testtes1.afp", "-rw-r--r-- 1 500 500 166 Nov 9 2001 73131-testtes1.AFP", "-rw-r--r-- 1 500 500 166 Nov 12 2001 73131-testtes2.afp", "-rw-r--r-- 1 500 500 166 Nov 12 2001 73131-testtes2.AFP", "-rw-r--r-- 1 500 500 2040000 Aug 5 07:35 testRemoteUPCopyNIX", "-rw-r--r-- 1 500 500 2040000 Aug 5 07:31 testRemoteUPDCopyNIX", "-rw-r--r-- 1 500 500 2040000 Aug 5 07:31 testRemoteUPVCopyNIX", "-rw-r--r-T 1 500 500 0 Mar 25 08:20 testSticky", "-rwxr-xr-t 1 500 500 0 Mar 25 08:21 testStickyExec", "-rwSr-Sr-- 1 500 500 0 Mar 25 08:22 testSuid", "-rwsr-sr-- 1 500 500 0 Mar 25 08:23 testSuidExec", "-rwsr-sr-- 1 500 500 0 Mar 25 0:23 testSuidExec2", "drwxrwx---+ 23 500 500 0 Jan 10 13:09 testACL", "-rw-r--r-- 1 1 3518644 May 25 12:12 std", "lrwxrwxrwx 1 neeme neeme 23 Mar 2 18:06 macros -> ./../../global/macros/.", "-rw-r--r-- 1 ftp group with spaces in it as allowed in cygwin see bug 38634" + " 83853 Jan 22 2001 zxJDBC-1.2.4.tar.gz", // Bug 38634 => NET-16 "crw-r----- 1 root kmem 0, 27 Jan 30 11:42 kmem", // FreeBSD device "crw------- 1 root sys 109,767 Jul 2 2004 pci@1c,600000:devctl", // Solaris device "-rwxrwx--- 1 ftp ftp-admin 816026400 Oct 5 2008 bloplab 7 cd1.img", // NET-294 // https://mail-archives.apache.org/mod_mbox/commons-dev/200408.mbox/%3c4122F3C1.9090402@tanukisoftware.com%3e "-rw-r--r-- 1 1 3518644 May 25 12:12 std", "-rw-rw---- 1 ep1adm sapsys 0 6\u6708 3\u65e5 2003\u5e74 \u8a66\u9a13\u30d5\u30a1\u30a4\u30eb.csv", "-rw-rw---- 1 ep1adm sapsys 0 8\u6708 17\u65e5 20:10 caerrinf", }; public UnixFTPEntryParserTest(final String name) { super(name); } private void checkPermissions(final FTPFile f) { assertTrue("Should have user read permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)); assertTrue("Should have user write permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have user execute permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertTrue("Should have group read permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Should NOT have group write permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have group execute permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)); assertTrue("Should have world read permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)); assertFalse("Should NOT have world write permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)); assertTrue("Should have world execute permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)); } @Override protected void doAdditionalGoodTests(final String test, final FTPFile f) { final String link = f.getLink(); if (null != link) { final int linklen = link.length(); if (linklen > 0) { assertEquals(link, test.substring(test.length() - linklen)); assertEquals(f.getType(), FTPFile.SYMBOLIC_LINK_TYPE); } } final int type = f.getType(); switch (test.charAt(0)) { case 'd': assertEquals("Type of " + test, type, FTPFile.DIRECTORY_TYPE); break; case 'l': assertEquals("Type of " + test, type, FTPFile.SYMBOLIC_LINK_TYPE); break; case 'b': case 'c': assertEquals(0, f.getHardLinkCount()); //$FALL-THROUGH$ TODO this needs to be fixed if a device type is introduced case 'f': case '-': assertEquals("Type of " + test, type, FTPFile.FILE_TYPE); break; default: assertEquals("Type of " + test, type, FTPFile.UNKNOWN_TYPE); } for (int access = FTPFile.USER_ACCESS; access <= FTPFile.WORLD_ACCESS; access++) { for (int perm = FTPFile.READ_PERMISSION; perm <= FTPFile.EXECUTE_PERMISSION; perm++) { final int pos = 3 * access + perm + 1; final char permchar = test.charAt(pos); assertEquals("Permission " + test.substring(1, 10), Boolean.valueOf(f.hasPermission(access, perm)), Boolean.valueOf(permchar != '-' && !Character.isUpperCase(permchar))); } } assertNotNull("Expected to find a timestamp", f.getTimestamp()); // Perhaps check date range (need to ensure all good examples qualify) // assertTrue(test,f.getTimestamp().get(Calendar.YEAR)>=2000); } @Override protected String[] getBadListing() { return badsamples; } @Override protected String[] getGoodListing() { return goodsamples; } @Override protected FTPFileEntryParser getParser() { return new UnixFTPEntryParser(); } public void testCorrectGroupNameParsing() { final FTPFile f = getParser().parseFTPEntry("-rw-r--r-- 1 ftpuser ftpusers 12414535 Mar 17 11:07 test 1999 abc.pdf"); assertNotNull(f); assertEquals(1, f.getHardLinkCount()); assertEquals("ftpuser", f.getUser()); assertEquals("ftpusers", f.getGroup()); assertEquals(12414535, f.getSize()); assertEquals("test 1999 abc.pdf", f.getName()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DAY_OF_MONTH, 17); cal.set(Calendar.HOUR_OF_DAY, 11); cal.set(Calendar.MINUTE, 7); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); assertEquals(f.getTimestamp().get(Calendar.MONTH), cal.get(Calendar.MONTH)); assertEquals(f.getTimestamp().get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_MONTH)); assertEquals(f.getTimestamp().get(Calendar.HOUR_OF_DAY), cal.get(Calendar.HOUR_OF_DAY)); assertEquals(f.getTimestamp().get(Calendar.MINUTE), cal.get(Calendar.MINUTE)); assertEquals(f.getTimestamp().get(Calendar.SECOND), cal.get(Calendar.SECOND)); } @Override public void testDefaultPrecision() { testPrecision("drwxr-xr-x 2 user group 4096 Mar 2 2014 zxbox", CalendarUnit.DAY_OF_MONTH); } public void testFilenamesWithEmbeddedNumbers() { final FTPFile f = getParser().parseFTPEntry("-rw-rw-rw- 1 user group 5840 Mar 19 09:34 123 456 abc.csv"); assertEquals("123 456 abc.csv", f.getName()); assertEquals(5840, f.getSize()); assertEquals("user", f.getUser()); assertEquals("group", f.getGroup()); } public void testGroupNameWithSpaces() { final FTPFile f = getParser().parseFTPEntry("drwx------ 4 maxm Domain Users 512 Oct 2 10:59 .metadata"); assertNotNull(f); assertEquals("maxm", f.getUser()); assertEquals("Domain Users", f.getGroup()); } public void testLeadingSpacesDefault() { // the default has been changed to keep spaces final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x 2 john smith group 4096 Mar 2 15:13 zxbox"); assertNotNull(f); assertEquals(" zxbox", f.getName()); // leading spaces retained } public void testLeadingSpacesNET566() { // check new behavior final FTPFile f = new UnixFTPEntryParser(null, false).parseFTPEntry("drwxr-xr-x 2 john smith group 4096 Mar 2 15:13 zxbox"); assertNotNull(f); assertEquals(" zxbox", f.getName()); // leading spaces retained } public void testNameWIthPunctuation() { final FTPFile f = getParser().parseFTPEntry("drwx------ 4 maxm Domain Users 512 Oct 2 10:59 abc(test)123.pdf"); assertNotNull(f); assertEquals("abc(test)123.pdf", f.getName()); } public void testNET294() { final FTPFile f = getParser().parseFTPEntry("-rwxrwx--- 1 ftp ftp-admin 816026400 Oct 5 2008 bloplab 7 cd1.img"); assertNotNull(f); assertEquals("ftp", f.getUser()); assertEquals("ftp-admin", f.getGroup()); assertEquals(816026400L, f.getSize()); assertNotNull("Timestamp should not be null", f.getTimestamp()); assertEquals(2008, f.getTimestamp().get(Calendar.YEAR)); assertEquals("bloplab 7 cd1.img", f.getName()); } public void testNoSpacesBeforeFileSize() { final FTPFile f = getParser().parseFTPEntry("drwxr-x---+1464 chrism chrism 41472 Feb 25 13:17 20090225"); assertNotNull(f); assertEquals(41472, f.getSize()); assertEquals(f.getType(), FTPFile.DIRECTORY_TYPE); assertEquals("chrism", f.getUser()); assertEquals("chrism", f.getGroup()); assertEquals(1464, f.getHardLinkCount()); } public void testNumericDateFormat() { final String testNumericDF = "-rw-r----- 1 neeme neeme 346 2005-04-08 11:22 services.vsp"; final String testNumericDF2 = "lrwxrwxrwx 1 neeme neeme 23 2005-03-02 18:06 macros -> ./../../global/macros/."; final UnixFTPEntryParser parser = new UnixFTPEntryParser(UnixFTPEntryParser.NUMERIC_DATE_CONFIG); final FTPFile f = parser.parseFTPEntry(testNumericDF); assertNotNull("Failed to parse " + testNumericDF, f); final Calendar cal = Calendar.getInstance(); cal.clear(); cal.set(Calendar.YEAR, 2005); cal.set(Calendar.MONTH, Calendar.APRIL); cal.set(Calendar.DAY_OF_MONTH, 8); cal.set(Calendar.HOUR_OF_DAY, 11); cal.set(Calendar.MINUTE, 22); assertEquals(cal.getTime(), f.getTimestamp().getTime()); final FTPFile f2 = parser.parseFTPEntry(testNumericDF2); assertNotNull("Failed to parse " + testNumericDF2, f2); assertEquals("symbolic link", "./../../global/macros/.", f2.getLink()); } public void testOwnerAndGroupNameWithSpaces() { final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x 2 john smith test group 4096 Mar 2 15:13 zxbox"); assertNotNull(f); assertEquals("john smith", f.getUser()); assertEquals("test group", f.getGroup()); } public void testOwnerNameWithSpaces() { final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x 2 john smith group 4096 Mar 2 15:13 zxbox"); assertNotNull(f); assertEquals("john smith", f.getUser()); } @Override public void testParseFieldsOnDirectory() throws Exception { final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x 2 user group 4096 Mar 2 15:13 zxbox"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a directory.", f.isDirectory()); checkPermissions(f); assertEquals(2, f.getHardLinkCount()); assertEquals("user", f.getUser()); assertEquals("group", f.getGroup()); assertEquals("zxbox", f.getName()); assertEquals(4096, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); if (f.getTimestamp().getTime().before(cal.getTime())) { cal.add(Calendar.YEAR, -1); } cal.set(Calendar.DAY_OF_MONTH, 2); cal.set(Calendar.HOUR_OF_DAY, 15); cal.set(Calendar.MINUTE, 13); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } @Override public void testParseFieldsOnFile() throws Exception { final FTPFile f = getParser().parseFTPEntry("-rwxr-xr-x 2 user my group 500 5000000000 Mar 2 15:13 zxbox"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a file.", f.isFile()); checkPermissions(f); assertEquals(2, f.getHardLinkCount()); assertEquals("user", f.getUser()); assertEquals("my group 500", f.getGroup()); assertEquals("zxbox", f.getName()); assertEquals(5000000000L, f.getSize()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); if (f.getTimestamp().getTime().before(cal.getTime())) { cal.add(Calendar.YEAR, -1); } cal.set(Calendar.DAY_OF_MONTH, 2); cal.set(Calendar.HOUR_OF_DAY, 15); cal.set(Calendar.MINUTE, 13); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } // https://mail-archives.apache.org/mod_mbox/commons-dev/200408.mbox/%3c4122F3C1.9090402@tanukisoftware.com%3e public void testParseFieldsOnFileJapaneseTime() { final FTPFile f = getParser().parseFTPEntry("-rwxr-xr-x 2 user group 4096 3\u6708 2\u65e5 15:13 zxbox"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a file.", f.isFile()); checkPermissions(f); assertEquals(2, f.getHardLinkCount()); assertEquals("user", f.getUser()); assertEquals("group", f.getGroup()); assertEquals("zxbox", f.getName()); assertEquals(4096, f.getSize()); assertNotNull("Timestamp not null", f.getTimestamp()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DATE, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); if (f.getTimestamp().getTime().before(cal.getTime())) { cal.add(Calendar.YEAR, -1); } cal.set(Calendar.DATE, 2); cal.set(Calendar.HOUR_OF_DAY, 15); cal.set(Calendar.MINUTE, 13); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } // https://mail-archives.apache.org/mod_mbox/commons-dev/200408.mbox/%3c4122F3C1.9090402@tanukisoftware.com%3e public void testParseFieldsOnFileJapaneseYear() { final FTPFile f = getParser().parseFTPEntry("-rwxr-xr-x 2 user group 4096 3\u6708 2\u65e5 2003\u5e74 \u8a66\u9a13\u30d5\u30a1\u30a4\u30eb.csv"); assertNotNull("Could not parse entry.", f); assertTrue("Should have been a file.", f.isFile()); checkPermissions(f); assertEquals(2, f.getHardLinkCount()); assertEquals("user", f.getUser()); assertEquals("group", f.getGroup()); assertEquals("\u8a66\u9a13\u30d5\u30a1\u30a4\u30eb.csv", f.getName()); assertEquals(4096, f.getSize()); assertNotNull("Timestamp not null", f.getTimestamp()); final Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, 2003); cal.set(Calendar.MONTH, Calendar.MARCH); cal.set(Calendar.DATE, 2); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime())); } @Override public void testRecentPrecision() { testPrecision("drwxr-xr-x 2 user group 4096 Mar 2 15:13 zxbox", CalendarUnit.MINUTE); } public void testTrailingSpaces() { final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x 2 john smith group 4096 Mar 2 15:13 zxbox "); assertNotNull(f); assertEquals("zxbox ", f.getName()); } public void testTrimLeadingSpacesNET566() { // check can trim spaces as before final FTPFile f = new UnixFTPEntryParser(null, true).parseFTPEntry("drwxr-xr-x 2 john smith group 4096 Mar 2 15:13 zxbox"); assertNotNull(f); assertEquals("zxbox", f.getName()); // leading spaces trimmed } } VMSFTPEntryParserTest.java000066400000000000000000000274241434047722200347040ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ftp/parser/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ftp.parser; import java.io.ByteArrayInputStream; import java.io.IOException; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPFileEntryParser; import org.apache.commons.net.ftp.FTPListParseEngine; /** */ public class VMSFTPEntryParserTest extends FTPParseTestFramework { private static final String[] BAD_SAMPLES = { "1-JUN.LIS;2 9/9 JUN-2-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)", "1-JUN.LIS;2 a/9 2-JUN-98 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)", "DATA.DIR; 1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (,RWED,RWED,RE)", "120196.TXT;1 118/126 14-APR-1997 12:45:27 PM [GROUP,OWNER] (RWED,,RWED,RE)", "30CHARBAR.TXT;1 11/18 2-JUN-1998 08:38:42 [GROUP-1,OWNER] (RWED,RWED,RWED,RE)", "A.;2 18/18 1-JUL-1998 08:43:20 [GROUP,OWNER] (RWED2,RWED,RWED,RE)", "AA.;2 152/153 13-FED-1997 08:13:43 [GROUP,OWNER] (RWED,RWED,RWED,RE)", "Directory USER1:[TEMP]\r\n\r\n", "\r\nTotal 14 files" }; // CHECKSTYLE:OFF (long lines) private static final String[] GOOD_SAMPLES = { "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", "1-JUN.LIS;3 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)", "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)", "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [TRANSLATED] (,RWED,RWED,RE)", "120196.TXT;1 118/126 14-APR-1997 12:45:27 [GROUP,OWNER] (RWED,,RWED,RE)", "30CHARBAR.TXT;1 11/18 2-JUN-1998 08:38:42 [GROUP,OWNER] (RWED,RWED,RWED,RE)", "A.;2 18/18 1-JUL-1998 08:43:20 [GROUP,OWNER] (RWED,RWED,RWED,RE)", "AA.;2 152/153 13-FEB-1997 08:13:43 [GROUP,OWNER] (RWED,RWED,RWED,RE)", "UCX$REXECD_STARTUP.LOG;1098\r\n" + " 4/15 24-FEB-2003 13:17:24 [POSTWARE,LP] (RWED,RWED,RE,)", "UNARCHIVE.COM;1 2/15 7-JUL-1997 16:37:45 [POSTWARE,LP] (RWE,RWE,RWE,RE)", "UNXMERGE.COM;15 1/15 20-AUG-1996 13:59:50 [POSTWARE,LP] (RWE,RWE,RWE,RE)", "UNXTEMP.COM;7 1/15 15-AUG-1996 14:10:38 [POSTWARE,LP] (RWE,RWE,RWE,RE)", "UNZIP_AND_ATTACH_FILES.COM;12\r\n" + " 14/15 24-JUL-2002 14:35:40 [TRANSLATED] (RWE,RWE,RWE,RE)", "UNZIP_AND_ATTACH_FILES.SAV;1\r\n" + " 14/15 17-JAN-2002 11:13:53 [POSTWARE,LP] (RWE,RWED,RWE,RE)", "FREEWARE40.DIR;1 27/36" + " 16-FEB-1999 10:01:46 [AP_HTTPD,APACHE$WWW (RWE,RWE,RE,RE)", "1-JUN.LIS;1 9/9 2-jun-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", "ALLOCMISS.COM;1 1 15-AUG-1996 14:10:38 [POSTWARE,LP] (RWE,RWE,RWE,RE)" }; // CHECKSTYLE:ON private static final String FULL_LISTING = "Directory USER1:[TEMP]\r\n\r\n" + "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)\r\n" + "2-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)\r\n" + "3-JUN.LIS;1 9/9 3-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)\r\n" + "3-JUN.LIS;4 9/9 7-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)\r\n" + "3-JUN.LIS;2 9/9 4-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)\r\n" + "3-JUN.LIS;3 9/9 6-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,)\r\n" + "\r\nTotal 6 files"; /** * @see junit.framework.TestCase#TestCase(String) */ public VMSFTPEntryParserTest(final String name) { super(name); } public void assertFileInListing(final FTPFile[] listing, final String name) { for (final FTPFile element : listing) { if (name.equals(element.getName())) { return; } } fail("File " + name + " not found in supplied listing"); } public void assertFileNotInListing(final FTPFile[] listing, final String name) { for (final FTPFile element : listing) { if (name.equals(element.getName())) { fail("Unexpected File " + name + " found in supplied listing"); } } } /* * Verify that the VMS parser does NOT set the permissions. */ private void checkPermisions(final FTPFile dir, final int octalPerm) { int permMask = 1 << 8; assertEquals("Owner should not have read permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)); permMask >>= 1; assertEquals("Owner should not have write permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)); permMask >>= 1; assertEquals("Owner should not have execute permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)); permMask >>= 1; assertEquals("Group should not have read permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)); permMask >>= 1; assertEquals("Group should not have write permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)); permMask >>= 1; assertEquals("Group should not have execute permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)); permMask >>= 1; assertEquals("World should not have read permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)); permMask >>= 1; assertEquals("World should not have write permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)); permMask >>= 1; assertEquals("World should not have execute permission.", (permMask & octalPerm) != 0, dir.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)); } @Override protected String[] getBadListing() { return BAD_SAMPLES; } @Override protected String[] getGoodListing() { return GOOD_SAMPLES; } @Override protected FTPFileEntryParser getParser() { final ConfigurableFTPFileEntryParserImpl parser = new VMSFTPEntryParser(); parser.configure(null); return parser; } protected FTPFileEntryParser getVersioningParser() { final ConfigurableFTPFileEntryParserImpl parser = new VMSVersioningFTPEntryParser(); parser.configure(null); return parser; } @Override public void testDefaultPrecision() { testPrecision("1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [TRANSLATED] (RWED,RD,,)", CalendarUnit.SECOND); } @Override public void testParseFieldsOnDirectory() throws Exception { FTPFile dir = getParser().parseFTPEntry("DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)"); assertTrue("Should be a directory.", dir.isDirectory()); assertEquals("DATA.DIR", dir.getName()); assertEquals(512, dir.getSize()); assertEquals("Tue Jun 02 07:32:04 1998", df.format(dir.getTimestamp().getTime())); assertEquals("GROUP", dir.getGroup()); assertEquals("OWNER", dir.getUser()); checkPermisions(dir, 0775); dir = getParser().parseFTPEntry("DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [TRANSLATED] (RWED,RWED,,RE)"); assertTrue("Should be a directory.", dir.isDirectory()); assertEquals("DATA.DIR", dir.getName()); assertEquals(512, dir.getSize()); assertEquals("Tue Jun 02 07:32:04 1998", df.format(dir.getTimestamp().getTime())); assertNull(dir.getGroup()); assertEquals("TRANSLATED", dir.getUser()); checkPermisions(dir, 0705); } @Override public void testParseFieldsOnFile() throws Exception { FTPFile file = getParser().parseFTPEntry("1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RW,R)"); assertTrue("Should be a file.", file.isFile()); assertEquals("1-JUN.LIS", file.getName()); assertEquals(9 * 512, file.getSize()); assertEquals("Tue Jun 02 07:32:04 1998", df.format(file.getTimestamp().getTime())); assertEquals("GROUP", file.getGroup()); assertEquals("OWNER", file.getUser()); checkPermisions(file, 0764); file = getParser().parseFTPEntry("1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [TRANSLATED] (RWED,RD,,)"); assertTrue("Should be a file.", file.isFile()); assertEquals("1-JUN.LIS", file.getName()); assertEquals(9 * 512, file.getSize()); assertEquals("Tue Jun 02 07:32:04 1998", df.format(file.getTimestamp().getTime())); assertNull(file.getGroup()); assertEquals("TRANSLATED", file.getUser()); checkPermisions(file, 0400); } @Override public void testRecentPrecision() { // Not used } public void testWholeListParse() throws IOException { final VMSFTPEntryParser parser = new VMSFTPEntryParser(); parser.configure(null); final FTPListParseEngine engine = new FTPListParseEngine(parser); engine.readServerList(new ByteArrayInputStream(FULL_LISTING.getBytes()), null); // use default encoding final FTPFile[] files = engine.getFiles(); assertEquals(6, files.length); assertFileInListing(files, "2-JUN.LIS"); assertFileInListing(files, "3-JUN.LIS"); assertFileInListing(files, "1-JUN.LIS"); assertFileNotInListing(files, "1-JUN.LIS;1"); } public void testWholeListParseWithVersioning() throws IOException { final VMSFTPEntryParser parser = new VMSVersioningFTPEntryParser(); parser.configure(null); final FTPListParseEngine engine = new FTPListParseEngine(parser); engine.readServerList(new ByteArrayInputStream(FULL_LISTING.getBytes()), null); // use default encoding final FTPFile[] files = engine.getFiles(); assertEquals(3, files.length); assertFileInListing(files, "1-JUN.LIS;1"); assertFileInListing(files, "2-JUN.LIS;1"); assertFileInListing(files, "3-JUN.LIS;4"); assertFileNotInListing(files, "3-JUN.LIS;1"); assertFileNotInListing(files, "3-JUN.LIS"); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/imap/000077500000000000000000000000001434047722200266725ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/imap/IMAPTest.java000066400000000000000000000034261434047722200311300ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.imap; import org.junit.Assert; import org.junit.Test; public class IMAPTest { @Test public void checkGenerator() { // This test assumes: // - 26 letters in the generator alphabet // - the generator uses a fixed size tag final IMAP imap = new IMAP(); final String initial = imap.generateCommandID(); int expected = 1; for (int j = 0; j < initial.length(); j++) { expected *= 26; // letters in alphabet } int i = 0; boolean matched = false; while (i <= expected + 10) { // don't loop forever, but allow it to pass go! i++; final String s = imap.generateCommandID(); matched = initial.equals(s); if (matched) { // we've wrapped around completely break; } } Assert.assertEquals(expected, i); Assert.assertTrue("Expected to see the original value again", matched); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/io/000077500000000000000000000000001434047722200263535ustar00rootroot00000000000000DotTerminatedMessageReaderTest.java000066400000000000000000000135631434047722200352020ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/io/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.io; import java.io.IOException; import java.io.StringReader; import junit.framework.TestCase; public class DotTerminatedMessageReaderTest extends TestCase { private static final String CRLF = "\r\n"; private static final String DOT = "."; private static final String EOM = CRLF + DOT + CRLF; private DotTerminatedMessageReader reader; private final StringBuilder str = new StringBuilder(); private final char[] buf = new char[64]; public void testDoubleCrBeforeDot() throws IOException { final String test = "Hello World!\r" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello World!\r" + CRLF, str.toString()); } public void testEmbeddedDot1() throws IOException { final String test = "Hello . World!" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello . World!" + CRLF, str.toString()); } public void testEmbeddedDot2() throws IOException { final String test = "Hello .. World!" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello .. World!" + CRLF, str.toString()); } public void testEmbeddedDot3() throws IOException { final String test = "Hello World." + CRLF + "more" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello World." + CRLF + "more" + CRLF, str.toString()); } public void testEmbeddedDot4() throws IOException { final String test = "Hello World\r.\nmore" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello World\r.\nmore" + CRLF, str.toString()); } public void testEmbeddedNewlines() throws IOException { final String test = "Hello" + CRLF + "World\nA\rB" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals(str.toString(), "Hello" + CRLF + "World\nA\rB" + CRLF); } public void testLeadingDot() throws IOException { final String test = "Hello World!" + CRLF + "..text" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello World!" + CRLF + ".text" + CRLF, str.toString()); } public void testReadLine1() throws Exception { final String test = "Hello World" + CRLF + "more" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); String line; while ((line = reader.readLine()) != null) { str.append(line); str.append("#"); } assertEquals("Hello World#more#", str.toString()); } public void testReadLine2() throws Exception { final String test = "Hello World\r.\nmore" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); String line; while ((line = reader.readLine()) != null) { str.append(line); str.append("#"); } assertEquals("Hello World\r.\nmore#", str.toString()); } public void testReadSimpleStringCrLfLineEnding() throws IOException { final String test = "Hello World!" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello World!" + CRLF, str.toString()); } public void testReadSimpleStringLfLineEnding() throws IOException { final String test = "Hello World!" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello World!" + CRLF, str.toString()); } public void testSingleDotWithTrailingText() throws IOException { final String test = "Hello World!" + CRLF + ".text" + EOM; reader = new DotTerminatedMessageReader(new StringReader(test)); int read = 0; while ((read = reader.read(buf)) != -1) { str.append(buf, 0, read); } assertEquals("Hello World!" + CRLF + ".text" + CRLF, str.toString()); } } ToNetASCIIInputStreamTest.java000066400000000000000000000071441434047722200340030ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/io/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.io; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; //import java.nio.charset.Charset; import org.junit.Assert; import org.junit.Test; public class ToNetASCIIInputStreamTest { private void byteTest(final boolean byByte, final String input, final String expect) throws IOException { final byte[] data = input.getBytes(StandardCharsets.US_ASCII); final byte[] expected = expect.getBytes(StandardCharsets.US_ASCII); final InputStream source = new ByteArrayInputStream(data); try (final ToNetASCIIInputStream toNetASCII = new ToNetASCIIInputStream(source)) { final byte[] output = new byte[data.length * 2]; // cannot be longer than twice the input final int length = byByte ? getSingleBytes(toNetASCII, output) : getBuffer(toNetASCII, output); final byte[] result = new byte[length]; System.arraycopy(output, 0, result, 0, length); Assert.assertArrayEquals("Failed converting " + input, expected, result); } } private int getBuffer(final ToNetASCIIInputStream toNetASCII, final byte[] output) throws IOException { int length = 0; int remain = output.length; int chunk; int offset = 0; while (remain > 0 && (chunk = toNetASCII.read(output, offset, remain)) != -1) { length += chunk; offset += chunk; remain -= chunk; } return length; } private int getSingleBytes(final ToNetASCIIInputStream toNetASCII, final byte[] output) throws IOException { int b; int length = 0; while ((b = toNetASCII.read()) != -1) { output[length++] = (byte) b; } return length; } @Test public void testToNetASCIIInputStream_single_bytes() throws Exception { byteTest(true, "", ""); byteTest(true, "\r", "\r"); byteTest(true, "\n", "\r\n"); byteTest(true, "a", "a"); byteTest(true, "a\nb", "a\r\nb"); byteTest(true, "a\r\nb", "a\r\nb"); byteTest(true, "Hello\nWorld\n", "Hello\r\nWorld\r\n"); byteTest(true, "Hello\nWorld\r\n", "Hello\r\nWorld\r\n"); byteTest(true, "Hello\nWorld\n\r", "Hello\r\nWorld\r\n\r"); } @Test public void testToNetASCIIInputStream1() throws Exception { byteTest(false, "", ""); byteTest(false, "\r", "\r"); byteTest(false, "a", "a"); byteTest(false, "a\nb", "a\r\nb"); byteTest(false, "a\r\nb", "a\r\nb"); byteTest(false, "\n", "\r\n"); byteTest(false, "Hello\nWorld\n", "Hello\r\nWorld\r\n"); byteTest(false, "Hello\nWorld\r\n", "Hello\r\nWorld\r\n"); byteTest(false, "Hello\nWorld\n\r", "Hello\r\nWorld\r\n\r"); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/nntp/000077500000000000000000000000001434047722200267235ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/nntp/TestThreader.java000066400000000000000000000047751434047722200322010ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.nntp; import java.util.Arrays; import java.util.List; import org.junit.Assert; import org.junit.Test; /** * Test the Threader */ public class TestThreader { private static final Threadable[] EMPTY_THREADABLE_ARRAY = {}; @SuppressWarnings("deprecation") // test of deprecated method @Test public void testEmptyArray() { // NET-539 final Threader t = new Threader(); final Threadable[] messages = EMPTY_THREADABLE_ARRAY; Assert.assertNull(t.thread(messages)); } @Test public void testEmptyIterable() { // NET-539 final Threader t = new Threader(); final Threadable[] messages = EMPTY_THREADABLE_ARRAY; final Iterable asList = Arrays.asList(messages); Assert.assertNull(t.thread(asList)); } @Test public void testEmptyList() { // NET-539 final Threader t = new Threader(); final Threadable[] messages = EMPTY_THREADABLE_ARRAY; final List asList = Arrays.asList(messages); Assert.assertNull(t.thread(asList)); } @Test @SuppressWarnings("deprecation") // test of deprecated method public void testNullArray() { // NET-539 final Threader t = new Threader(); final Threadable[] messages = null; Assert.assertNull(t.thread(messages)); } @Test public void testNullIterable() { final Threader t = new Threader(); final Iterable messages = null; Assert.assertNull(t.thread(messages)); } @Test public void testNullList() { final Threader t = new Threader(); final List messages = null; Assert.assertNull(t.thread(messages)); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ntp/000077500000000000000000000000001434047722200265455ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ntp/TestNtpClient.java000066400000000000000000000101161434047722200321470ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import java.io.IOException; import java.net.InetAddress; import org.apache.commons.net.examples.ntp.SimpleNTPServer; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * JUnit test class for NtpClient using SimpleNTPServer */ public class TestNtpClient { private static SimpleNTPServer server; @BeforeClass public static void oneTimeSetUp() throws IOException { // one-time initialization code server = new SimpleNTPServer(0); server.connect(); try { server.start(); } catch (final IOException e) { Assert.fail("failed to start NTP server: " + e); } Assert.assertTrue(server.isStarted()); // System.out.println("XXX: time server started"); boolean running = false; for (int retries = 0; retries < 5; retries++) { running = server.isRunning(); if (running) { break; } // if not running then sleep 2 seconds and try again try { Thread.sleep(2000); } catch (final InterruptedException e) { // ignore } } Assert.assertTrue(running); } @AfterClass public static void oneTimeTearDown() { // one-time cleanup code if (server != null) { server.stop(); server = null; } } @Test public void testGetTime() throws IOException { final long currentTimeMillis = System.currentTimeMillis(); final NTPUDPClient client = new NTPUDPClient(); // timeout if response takes longer than 2 seconds client.setDefaultTimeout(2000); try { // Java 1.7: use InetAddress.getLoopbackAddress() instead final InetAddress addr = InetAddress.getByAddress("loopback", new byte[] { 127, 0, 0, 1 }); final TimeInfo timeInfo = client.getTime(addr, server.getPort()); Assert.assertNotNull(timeInfo); Assert.assertTrue(timeInfo.getReturnTime() >= currentTimeMillis); final NtpV3Packet message = timeInfo.getMessage(); Assert.assertNotNull(message); final TimeStamp rcvTimeStamp = message.getReceiveTimeStamp(); final TimeStamp xmitTimeStamp = message.getTransmitTimeStamp(); Assert.assertTrue(xmitTimeStamp.compareTo(rcvTimeStamp) >= 0); final TimeStamp originateTimeStamp = message.getOriginateTimeStamp(); Assert.assertNotNull(originateTimeStamp); Assert.assertTrue(originateTimeStamp.getTime() >= currentTimeMillis); Assert.assertEquals(NtpV3Packet.MODE_SERVER, message.getMode()); // following assertions are specific to the SimpleNTPServer final TimeStamp referenceTimeStamp = message.getReferenceTimeStamp(); Assert.assertNotNull(referenceTimeStamp); Assert.assertTrue(referenceTimeStamp.getTime() >= currentTimeMillis); Assert.assertEquals(NtpV3Packet.VERSION_3, message.getVersion()); Assert.assertEquals(1, message.getStratum()); Assert.assertEquals("LCL", NtpUtils.getReferenceClock(message)); } finally { client.close(); } } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ntp/TestNtpPacket.java000066400000000000000000000200351434047722200321410ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import static org.junit.jupiter.api.Assertions.assertThrows; import java.net.DatagramPacket; import org.junit.Assert; import org.junit.Test; public class TestNtpPacket { // pre-canned NTP packet // [version:3, mode:4, poll:4, refId=0x81531472, precision:-17, delay:100, dispersion(ms):51.605224609375, // id:129.83.20.114, xmitTime:Thu, May 30 2013 17:46:01.295, etc. ] static final byte[] ntpPacket = hexStringToByteArray("1c0304ef0000006400000d3681531472d552447fec1d6000d5524718ac49ba5ed55247194b6d9000d55247194b797000"); private static byte[] hexStringToByteArray(final String s) { final int len = s.length(); final byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } @Test public void testCreate() { final NtpV3Packet message = new NtpV3Impl(); message.setLeapIndicator(0); // byte 0 [bit numbers 7-6] message.setVersion(NtpV3Packet.VERSION_3); // byte 0 [bit numbers 5-4] message.setMode(4); // byte 0 [bit numbers 3-0] message.setStratum(3); // byte 1 message.setPoll(4); // byte 2 message.setPrecision(-17); // byte 3 message.setRootDelay(100); // bytes 4-7 message.setRootDispersion(3382); // bytes 8-11 message.setReferenceId(0x81531472); // byte 12-15 message.setReferenceTime(new TimeStamp(0xd552447fec1d6000L)); message.setOriginateTimeStamp(new TimeStamp(0xd5524718ac49ba5eL)); message.setReceiveTimeStamp(new TimeStamp(0xd55247194b6d9000L)); message.setTransmitTime(new TimeStamp(0xd55247194b797000L)); Assert.assertEquals(-17, message.getPrecision()); Assert.assertEquals(4, message.getPoll()); Assert.assertEquals(100, message.getRootDelay()); Assert.assertEquals(3382, message.getRootDispersion()); Assert.assertEquals(0x81531472, message.getReferenceId()); Assert.assertNotNull(message.getReferenceTimeStamp()); Assert.assertEquals("NTP", message.getType()); Assert.assertEquals("Server", message.getModeName()); Assert.assertEquals("129.83.20.114", message.getReferenceIdString()); Assert.assertEquals(51, message.getRootDispersionInMillis()); Assert.assertEquals(message.getRootDelay() / 65.536, message.getRootDelayInMillisDouble(), 1e-13); final DatagramPacket dp = message.getDatagramPacket(); // this creates a new datagram Assert.assertNotNull(dp); Assert.assertEquals(48, dp.getLength()); // fixed 48-byte length final NtpV3Packet message2 = new NtpV3Impl(); final DatagramPacket dp2 = new DatagramPacket(ntpPacket, ntpPacket.length); message2.setDatagramPacket(dp2); Assert.assertEquals(message2, message); Assert.assertEquals(message2.hashCode(), message.hashCode()); Assert.assertEquals(message2.toString(), message.toString()); } @Test public void testCreateAndSetByte0() { // LI + VN + Mode all part of first byte -- make sure set order does not matter final NtpV3Packet message = new NtpV3Impl(); message.setLeapIndicator(2); message.setMode(4); message.setVersion(NtpV3Packet.VERSION_3); Assert.assertEquals(4, message.getMode()); Assert.assertEquals(NtpV3Packet.VERSION_3, message.getVersion()); Assert.assertEquals(2, message.getLeapIndicator()); message.setLeapIndicator(2); message.setVersion(NtpV3Packet.VERSION_3); message.setMode(4); Assert.assertEquals(4, message.getMode()); Assert.assertEquals(NtpV3Packet.VERSION_3, message.getVersion()); Assert.assertEquals(2, message.getLeapIndicator()); message.setMode(4); message.setLeapIndicator(2); message.setVersion(NtpV3Packet.VERSION_3); Assert.assertEquals(4, message.getMode()); Assert.assertEquals(NtpV3Packet.VERSION_3, message.getVersion()); Assert.assertEquals(2, message.getLeapIndicator()); message.setMode(4); message.setVersion(NtpV3Packet.VERSION_3); message.setLeapIndicator(2); Assert.assertEquals(4, message.getMode()); Assert.assertEquals(NtpV3Packet.VERSION_3, message.getVersion()); Assert.assertEquals(2, message.getLeapIndicator()); message.setVersion(NtpV3Packet.VERSION_3); message.setMode(4); message.setLeapIndicator(2); Assert.assertEquals(4, message.getMode()); Assert.assertEquals(NtpV3Packet.VERSION_3, message.getVersion()); Assert.assertEquals(2, message.getLeapIndicator()); message.setVersion(NtpV3Packet.VERSION_3); message.setLeapIndicator(2); message.setMode(4); Assert.assertEquals(4, message.getMode()); Assert.assertEquals(NtpV3Packet.VERSION_3, message.getVersion()); Assert.assertEquals(2, message.getLeapIndicator()); } @Test public void testCreateFromBadPacket() { final NtpV3Packet message = new NtpV3Impl(); final DatagramPacket dp = new DatagramPacket(ntpPacket, ntpPacket.length - 4); // drop 4-bytes from packet assertThrows(IllegalArgumentException.class, () -> message.setDatagramPacket(dp)); } @Test public void testCreateFromBytes() { final NtpV3Packet message = new NtpV3Impl(); final DatagramPacket dp = new DatagramPacket(ntpPacket, ntpPacket.length); message.setDatagramPacket(dp); Assert.assertEquals(4, message.getMode()); } @Test public void testCreateFromNullPacket() { final NtpV3Packet message = new NtpV3Impl(); assertThrows(IllegalArgumentException.class, () -> message.setDatagramPacket(null)); } @Test public void testCreateNtpV4() { final NtpV3Packet message = new NtpV3Impl(); message.setVersion(NtpV3Packet.VERSION_4); message.setStratum(3); message.setReferenceId(0x81531472); // force hex-string reference id string Assert.assertEquals("81531472", message.getReferenceIdString()); message.setVersion(NtpV3Packet.VERSION_4); message.setStratum(1); message.setReferenceId(0x55534E4F); // USNO // force raw-string reference id string Assert.assertEquals("USNO", message.getReferenceIdString()); message.setReferenceId(0x47505300); // GPS Assert.assertEquals("GPS", message.getReferenceIdString()); } @Test public void testEquals() { final NtpV3Packet message1 = new NtpV3Impl(); final DatagramPacket dp = new DatagramPacket(ntpPacket, ntpPacket.length); message1.setDatagramPacket(dp); final NtpV3Packet message2 = new NtpV3Impl(); message2.setDatagramPacket(dp); Assert.assertEquals("hashCode", message1.hashCode(), message2.hashCode()); Assert.assertEquals(message1, message2); // now change the packet to force equals() => false message2.setMode(2); Assert.assertTrue(message1.getMode() != message2.getMode()); Assert.assertNotEquals(message1, message2); final NtpV3Packet message3 = null; Assert.assertNotEquals(message3, message1); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ntp/TestTimeInfo.java000066400000000000000000000125051434047722200317650ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import static org.junit.jupiter.api.Assertions.assertThrows; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Test; public class TestTimeInfo { @Test public void testAddress() throws UnknownHostException { final NtpV3Packet packet = new NtpV3Impl(); final TimeInfo info = new TimeInfo(packet, System.currentTimeMillis()); Assert.assertNull(info.getAddress()); packet.getDatagramPacket().setAddress(InetAddress.getByAddress("loopback", new byte[] { 127, 0, 0, 1 })); Assert.assertNotNull(info.getAddress()); } @Test public void testComputeDetails() { // if (origTime > returnTime) // assert destTime >= origTime final NtpV3Packet packet = new NtpV3Impl(); final long returnTimeMillis = System.currentTimeMillis(); // example // returntime=1370571658178 // origTime= 1370571659178 // originate time as defined in RFC-1305 (t1) packet.setOriginateTimeStamp(TimeStamp.getNtpTime(returnTimeMillis + 1000)); // Receive Time is time request received by server (t2) packet.setReceiveTimeStamp(packet.getOriginateTimeStamp()); // Transmit time is time reply sent by server (t3) packet.setTransmitTime(packet.getOriginateTimeStamp()); packet.setReferenceTime(packet.getOriginateTimeStamp()); // long origTime = packet.getOriginateTimeStamp().getTime(); // System.out.println("returntime=" + returnTime); // System.out.println("origTime= " + origTime); final TimeInfo info = new TimeInfo(packet, returnTimeMillis); info.computeDetails(); Assert.assertSame(packet, info.getMessage()); Assert.assertEquals(returnTimeMillis, info.getReturnTime()); Assert.assertEquals(Long.valueOf(500), info.getOffset()); Assert.assertEquals(Long.valueOf(-1000), info.getDelay()); // comments: [Warning: processing time > total network time, Error: OrigTime > DestRcvTime] Assert.assertEquals(2, info.getComments().size()); } @Test public void testEquals() { final NtpV3Packet packet = new NtpV3Impl(); final long returnTime = System.currentTimeMillis(); final TimeInfo info = new TimeInfo(packet, returnTime); info.addComment("this is a comment"); final TimeInfo other = new TimeInfo(packet, returnTime); other.addComment("this is a comment"); Assert.assertEquals(info, other); // fails Assert.assertEquals(info.hashCode(), other.hashCode()); other.addComment("another comment"); // Assert.assertFalse(info.equals(other)); // comments not used for equality final TimeInfo another = new TimeInfo(packet, returnTime, new ArrayList()); Assert.assertEquals(info, another); } @Test public void testException() { final NtpV3Packet packet = null; assertThrows(IllegalArgumentException.class, () -> new TimeInfo(packet, 1L)); } @Test public void testNotEquals() { final NtpV3Packet packet = new NtpV3Impl(); final long returnTime = System.currentTimeMillis(); final TimeInfo info = new TimeInfo(packet, returnTime); // 1. different return time final NtpV3Packet packet2 = new NtpV3Impl(); Assert.assertEquals(packet, packet2); final TimeInfo info2 = new TimeInfo(packet2, returnTime + 1); Assert.assertNotEquals(info, info2); // 2. different message / same time packet2.setStratum(3); packet2.setRootDelay(25); final TimeInfo info3 = new TimeInfo(packet2, returnTime); Assert.assertNotEquals(info, info3); // 3. different class Object other = this; Assert.assertNotEquals(info, other); // 4. null comparison other = null; Assert.assertNotEquals(info, other); } @Test public void testZeroTime() { final NtpV3Packet packet = new NtpV3Impl(); final TimeInfo info = new TimeInfo(packet, 0); info.computeDetails(); Assert.assertNull(info.getDelay()); Assert.assertNull(info.getOffset()); Assert.assertEquals(0L, info.getReturnTime()); // comments: Error: zero orig time -- cannot compute delay/offset final List comments = info.getComments(); Assert.assertEquals(1, comments.size()); Assert.assertTrue(comments.get(0).contains("zero orig time")); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/ntp/TimeStampTest.java000066400000000000000000000100771434047722200321600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.ntp; import java.util.Calendar; import java.util.Date; import junit.framework.TestCase; /** * Test class that validates assertions for the basic TimeStamp operations and comparisons. */ public class TimeStampTest extends TestCase { private static final String TIME1 = "c1a9ae1c.cf6ac48d"; // Tue, Dec 17 2002 14:07:24.810 UTC private static final String TIME2 = "c1a9ae1c.cf6ac48f"; // Tue, Dec 17 2002 14:07:24.810 UTC private static final String TIME3 = "c1a9ae1d.cf6ac48e"; // Tue, Dec 17 2002 14:07:25.810 UTC public void testCompare() { final TimeStamp ts1 = new TimeStamp(TIME1); // Tue, Dec 17 2002 14:07:24.810 UTC final TimeStamp ts2 = new TimeStamp(TIME1); final TimeStamp ts3 = new TimeStamp(TIME2); // Tue, Dec 17 2002 14:07:24.810 UTC final TimeStamp ts4 = new TimeStamp(TIME3); // Tue, Dec 17 2002 14:07:25.810 UTC // do assertion tests on TimeStamp class assertEquals("equals(1,2)", ts1, ts2); assertEquals("compareTo(1,2)", 0, ts1.compareTo(ts2)); assertEquals("ntpValue(1,2)", ts1.ntpValue(), ts2.ntpValue()); assertEquals("hashCode(1,2)", ts1.hashCode(), ts2.hashCode()); assertEquals("ts1==ts1", ts1, ts1); // timestamps in ts1 (TIME1) and ts3 (TIME2) are only off by the smallest // fraction of a second (~200 picoseconds) so the times are not equal but // when converted to Java dates (in milliseconds) they will be equal. assertFalse("ts1 != ts3", ts1.equals(ts3)); assertEquals("compareTo(1,3)", -1, ts1.compareTo(ts3)); assertEquals("seconds", ts1.getSeconds(), ts3.getSeconds()); assertTrue("fraction", ts1.getFraction() != ts3.getFraction()); assertTrue("ntpValue(1,3)", ts1.ntpValue() != ts3.ntpValue()); assertTrue("hashCode(1,3)", ts1.hashCode() != ts3.hashCode()); final long time1 = ts1.getTime(); final long time3 = ts3.getTime(); assertEquals("equals(time1,3)", time1, time3); // ntpTime1 != ntpTime3 but JavaTime(t1) == JavaTime(t3)... assertFalse("ts3 != ts4", ts3.equals(ts4)); assertTrue("time3 != ts4.time", time3 != ts4.getTime()); } public void testDateConversion() { // convert current date to NtpTimeStamp then compare Java date // computed from NTP timestamp with original Java date. final Calendar refCal = Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC")); final Date refDate = refCal.getTime(); final TimeStamp ts = new TimeStamp(refDate); assertEquals("refDate.getTime()", refDate.getTime(), ts.getTime()); final Date tsDate = ts.getDate(); assertEquals(refDate, tsDate); } public void testNotSame() { final TimeStamp time = TimeStamp.getCurrentTime(); Object other = Integer.valueOf(0); if (time.equals(other)) { fail("TimeStamp cannot equal Date"); } other = null; if (time.equals(other)) { fail("TimeStamp cannot equal null"); } } public void testUTCString() { final TimeStamp ts1 = new TimeStamp(TIME1); // Tue, Dec 17 2002 14:07:24.810 UTC final String actual = ts1.toUTCString(); assertEquals("Tue, Dec 17 2002 14:07:24.810 UTC", actual); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/pop3/000077500000000000000000000000001434047722200266255ustar00rootroot00000000000000POP3ClientCommandsTest.java000066400000000000000000000354761434047722200336320ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/pop3/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.IOException; import java.io.Reader; import java.net.InetAddress; import junit.framework.TestCase; /** * * The POP3* tests all presume the existence of the following parameters: mailserver: localhost (running on the default port 110) account: username=test; * password=password account: username=alwaysempty; password=password. mail: At least four emails in the test account and zero emails in the alwaysempty account * * If this won't work for you, you can change these parameters in the TestSetupParameters class. * * The tests were originally run on a default installation of James. Your mileage may vary based on the POP3 server you run the tests against. Some servers are * more standards-compliant than others. */ public class POP3ClientCommandsTest extends TestCase { POP3Client pop3Client; String user = POP3Constants.user; String emptyUser = POP3Constants.emptyuser; String password = POP3Constants.password; String mailhost = POP3Constants.mailhost; public POP3ClientCommandsTest(final String name) { super(name); } private void connect() throws Exception { pop3Client.connect(InetAddress.getByName(mailhost)); assertTrue(pop3Client.isConnected()); assertEquals(POP3.AUTHORIZATION_STATE, pop3Client.getState()); } private void login() throws Exception { assertTrue(pop3Client.login(user, password)); assertEquals(POP3.TRANSACTION_STATE, pop3Client.getState()); } private void reset() throws IOException { // Case where this is the first time reset is called if (pop3Client == null) { // Do nothing } else if (pop3Client.isConnected()) { pop3Client.disconnect(); } pop3Client = null; pop3Client = new POP3Client(); } public void testDelete() throws Exception { reset(); connect(); login(); // Get the original number of messages POP3MessageInfo[] msg = pop3Client.listMessages(); final int numMessages = msg.length; int numDeleted = 0; // Now delete some and logout for (int i = 0; i < numMessages - 3; i++) { pop3Client.deleteMessage(i + 1); numDeleted++; } // Check to see that they are marked as deleted assertEquals(numMessages, numDeleted + 3); // Logout and come back in pop3Client.logout(); reset(); connect(); login(); // Get the new number of messages, because of // reset, new number should match old number msg = pop3Client.listMessages(); assertEquals(numMessages - numDeleted, msg.length); } public void testDeleteWithReset() throws Exception { reset(); connect(); login(); // Get the original number of messages POP3MessageInfo[] msg = pop3Client.listMessages(); final int numMessages = msg.length; int numDeleted = 0; // Now delete some and logout for (int i = 0; i < numMessages - 1; i++) { pop3Client.deleteMessage(i + 1); numDeleted++; } // Check to see that they are marked as deleted assertEquals(numMessages, numDeleted + 1); // Now reset to unmark the messages as deleted pop3Client.reset(); // Logout and come back in pop3Client.logout(); reset(); connect(); login(); // Get the new number of messages, because of // reset, new number should match old number msg = pop3Client.listMessages(); assertEquals(numMessages, msg.length); } public void testListMessageOnEmptyMailbox() throws Exception { reset(); connect(); assertTrue(pop3Client.login(emptyUser, password)); // The first message is always at index 1 final POP3MessageInfo msg = pop3Client.listMessage(1); assertNull(msg); } public void testListMessageOnFullMailbox() throws Exception { reset(); connect(); login(); // The first message is always at index 1 POP3MessageInfo msg = pop3Client.listMessage(1); assertNotNull(msg); assertEquals(1, msg.number); assertTrue(msg.size > 0); assertNull(msg.identifier); // Now retrieve a message from index 0 msg = pop3Client.listMessage(0); assertNull(msg); // Now retrieve a msg that is not there msg = pop3Client.listMessage(100000); assertNull(msg); // Now retrieve a msg with a negative index msg = pop3Client.listMessage(-2); assertNull(msg); // Now try to get a valid message from the update state pop3Client.setState(POP3.UPDATE_STATE); msg = pop3Client.listMessage(1); assertNull(msg); } public void testListMessagesOnEmptyMailbox() throws Exception { reset(); connect(); assertTrue(pop3Client.login(emptyUser, password)); POP3MessageInfo[] msg = pop3Client.listMessages(); assertEquals(0, msg.length); // Now test from the update state pop3Client.setState(POP3.UPDATE_STATE); msg = pop3Client.listMessages(); assertNull(msg); } public void testListMessagesOnFullMailbox() throws Exception { reset(); connect(); login(); POP3MessageInfo[] msg = pop3Client.listMessages(); assertTrue(msg.length > 0); for (int i = 0; i < msg.length; i++) { assertNotNull(msg[i]); assertEquals(i + 1, msg[i].number); assertTrue(msg[i].size > 0); assertNull(msg[i].identifier); } // Now test from the update state pop3Client.setState(POP3.UPDATE_STATE); msg = pop3Client.listMessages(); assertNull(msg); } public void testListUniqueIdentifierOnEmptyMailbox() throws Exception { reset(); connect(); assertTrue(pop3Client.login(emptyUser, password)); // The first message is always at index 1 final POP3MessageInfo msg = pop3Client.listUniqueIdentifier(1); assertNull(msg); } public void testListUniqueIDOnFullMailbox() throws Exception { reset(); connect(); login(); // The first message is always at index 1 POP3MessageInfo msg = pop3Client.listUniqueIdentifier(1); assertNotNull(msg); assertEquals(1, msg.number); assertNotNull(msg.identifier); // Now retrieve a message from index 0 msg = pop3Client.listUniqueIdentifier(0); assertNull(msg); // Now retrieve a msg that is not there msg = pop3Client.listUniqueIdentifier(100000); assertNull(msg); // Now retrieve a msg with a negative index msg = pop3Client.listUniqueIdentifier(-2); assertNull(msg); // Now try to get a valid message from the update state pop3Client.setState(POP3.UPDATE_STATE); msg = pop3Client.listUniqueIdentifier(1); assertNull(msg); } public void testListUniqueIDsOnEmptyMailbox() throws Exception { reset(); connect(); assertTrue(pop3Client.login(emptyUser, password)); POP3MessageInfo[] msg = pop3Client.listUniqueIdentifiers(); assertEquals(0, msg.length); // Now test from the update state pop3Client.setState(POP3.UPDATE_STATE); msg = pop3Client.listUniqueIdentifiers(); assertNull(msg); } public void testListUniqueIDsOnFullMailbox() throws Exception { reset(); connect(); login(); POP3MessageInfo[] msg = pop3Client.listUniqueIdentifiers(); assertTrue(msg.length > 0); for (int i = 0; i < msg.length; i++) { assertNotNull(msg[i]); assertEquals(i + 1, msg[i].number); assertNotNull(msg[i].identifier); } // Now test from the update state pop3Client.setState(POP3.UPDATE_STATE); msg = pop3Client.listUniqueIdentifiers(); assertNull(msg); } public void testNoopCommand() throws Exception { reset(); connect(); // Should fail before authorization assertFalse(pop3Client.noop()); // Should pass in transaction state login(); assertTrue(pop3Client.noop()); // Should fail in update state pop3Client.setState(POP3.UPDATE_STATE); assertFalse(pop3Client.noop()); } public void testResetAndDeleteShouldFails() throws Exception { reset(); connect(); login(); pop3Client.setState(POP3.UPDATE_STATE); assertFalse(pop3Client.reset()); assertFalse(pop3Client.deleteMessage(1)); } public void testRetrieveMessageOnEmptyMailbox() throws Exception { reset(); connect(); assertTrue(pop3Client.login(emptyUser, password)); assertNull(pop3Client.retrieveMessage(1)); } public void testRetrieveMessageOnFullMailbox() throws Exception { reset(); connect(); login(); int reportedSize = 0; int actualSize = 0; final POP3MessageInfo[] msg = pop3Client.listMessages(); assertTrue(msg.length > 0); for (int i = msg.length; i > 0; i--) { reportedSize = msg[i - 1].size; final Reader r = pop3Client.retrieveMessage(i); assertNotNull(r); int delaycount = 0; if (!r.ready()) { // Give the reader time to get the message // from the server Thread.sleep(500); delaycount++; // but don't wait too long if (delaycount == 4) { break; } } while (r.ready()) { r.read(); actualSize++; } // Due to variations in line termination // on different platforms, the actual // size may vary slightly. On Win2KPro, the // actual size is 2 bytes larger than the reported // size. assertTrue(actualSize >= reportedSize); } } public void testRetrieveMessageShouldFails() throws Exception { reset(); connect(); login(); // Try to get message 0 assertNull(pop3Client.retrieveMessage(0)); // Try to get a negative message assertNull(pop3Client.retrieveMessage(-2)); // Try to get a message that is not there assertNull(pop3Client.retrieveMessage(100000)); // Change states and try to get a valid message pop3Client.setState(POP3.UPDATE_STATE); assertNull(pop3Client.retrieveMessage(1)); } public void testRetrieveMessageTopOnEmptyMailbox() throws Exception { reset(); connect(); assertTrue(pop3Client.login(emptyUser, password)); assertNull(pop3Client.retrieveMessageTop(1, 10)); } public void testRetrieveMessageTopOnFullMailbox() throws Exception { reset(); connect(); login(); final int numLines = 10; final POP3MessageInfo[] msg = pop3Client.listMessages(); assertTrue(msg.length > 0); for (int i = 0; i < msg.length; i++) { Reader r = pop3Client.retrieveMessageTop(i + 1, numLines); assertNotNull(r); r.close(); r = null; } } public void testRetrieveMessageTopShouldFails() throws Exception { reset(); connect(); login(); // Try to get message 0 assertNull(pop3Client.retrieveMessageTop(0, 10)); // Try to get a negative message assertNull(pop3Client.retrieveMessageTop(-2, 10)); // Try to get a message that is not there assertNull(pop3Client.retrieveMessageTop(100000, 10)); // Change states and try to get a valid message pop3Client.setState(POP3.UPDATE_STATE); assertNull(pop3Client.retrieveMessageTop(1, 10)); } public void testRetrieveOverSizedMessageTopOnFullMailbox() throws Exception { reset(); connect(); login(); int actualSize = 0; final POP3MessageInfo msg = pop3Client.listMessage(1); final int reportedSize = msg.size; // Now try to retrieve more lines than exist in the message final Reader r = pop3Client.retrieveMessageTop(1, 100000); assertNotNull(r); int delaycount = 0; while (!r.ready()) { // Give the reader time to get the message // from the server Thread.sleep(500); delaycount++; // but don't wait too long if (delaycount == 4) { break; } } while (r.ready()) { r.read(); actualSize++; } // Due to variations in line termination // on different platforms, the actual // size may vary slightly. On Win2KPro, the // actual size is 2 bytes larger than the reported // size. assertTrue(actualSize >= reportedSize); } public void testStatus() throws Exception { reset(); connect(); // Should fail in authorization state assertNull(pop3Client.status()); // Should pass on a mailbox with mail in it login(); final POP3MessageInfo msg = pop3Client.status(); assertTrue(msg.number > 0); assertTrue(msg.size > 0); assertNull(msg.identifier); pop3Client.logout(); // Should also pass on a mailbox with no mail in it reset(); connect(); assertTrue(pop3Client.login(emptyUser, password)); final POP3MessageInfo msg2 = pop3Client.status(); assertEquals(0, msg2.number); assertEquals(0, msg2.size); assertNull(msg2.identifier); pop3Client.logout(); // Should fail in the 'update' state reset(); connect(); login(); pop3Client.setState(POP3.UPDATE_STATE); assertNull(pop3Client.status()); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/pop3/POP3ClientTest.java000066400000000000000000000104231434047722200322100ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.IOException; import java.net.InetAddress; import junit.framework.TestCase; /** * * The POP3* tests all presume the existence of the following parameters: mailserver: localhost (running on the default port 110) account: username=test; * password=password account: username=alwaysempty; password=password. mail: At least four emails in the test account and zero emails in the alwaysempty account * * If this won't work for you, you can change these parameters in the TestSetupParameters class. * * The tests were originally run on a default installation of James. Your mileage may vary based on the POP3 server you run the tests against. Some servers are * more standards-compliant than others. */ public class POP3ClientTest extends TestCase { POP3Client p; String user = POP3Constants.user; String emptyUser = POP3Constants.emptyuser; String password = POP3Constants.password; String mailhost = POP3Constants.mailhost; public POP3ClientTest(final String name) { super(name); } private void connect() throws Exception { p.connect(InetAddress.getByName(mailhost)); assertTrue(p.isConnected()); assertEquals(POP3.AUTHORIZATION_STATE, p.getState()); } private void login() throws Exception { assertTrue(p.login(user, password)); assertEquals(POP3.TRANSACTION_STATE, p.getState()); } private void reset() throws IOException { // Case where this is the first time reset is called if (p == null) { // Do nothing } else if (p.isConnected()) { p.disconnect(); } p = null; p = new POP3Client(); } public void testInvalidLoginWithBadName() throws Exception { reset(); connect(); // Try with an invalid user that doesn't exist assertFalse(p.login("badusername", password)); } public void testInvalidLoginWithBadPassword() throws Exception { reset(); connect(); // Try with a bad password assertFalse(p.login(user, "badpassword")); } /* * Test to try to run the login method from the disconnected, transaction and update states */ public void testLoginFromWrongState() throws Exception { reset(); // Not currently connected, not in authorization state // Try to login with good name/password assertFalse(p.login(user, password)); // Now connect and set the state to 'transaction' and try again connect(); p.setState(POP3.TRANSACTION_STATE); assertFalse(p.login(user, password)); p.disconnect(); // Now connect and set the state to 'update' and try again connect(); p.setState(POP3.UPDATE_STATE); assertFalse(p.login(user, password)); p.disconnect(); } public void testLogoutFromAllStates() throws Exception { // From 'transaction' state reset(); connect(); login(); assertTrue(p.logout()); assertEquals(POP3.UPDATE_STATE, p.getState()); // From 'update' state reset(); connect(); login(); p.setState(POP3.UPDATE_STATE); assertTrue(p.logout()); } /* * Simple test to logon to a valid server using a valid user name and password. */ public void testValidLoginWithNameAndPassword() throws Exception { reset(); connect(); // Try with a valid user login(); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/pop3/POP3Constants.java000066400000000000000000000033671434047722200321170ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; /** * * The POP3* tests all presume the existence of the following parameters: mailserver: localhost (running on the default port 110) account: username=test; * password=password account: username=alwaysempty; password=password. mail: At least four emails in the test account and zero emails in the alwaysempty account * * If this won't work for you, you can change these parameters in the TestSetupParameters class. * * The tests were originally run on a default installation of James. Your mileage may vary based on the POP3 server you run the tests against. Some servers are * more standards-compliant than others. */ public class POP3Constants { public static final String user = "test"; public static final String emptyuser = "alwaysempty"; public static final String password = "password"; public static final String mailhost = "localhost"; // Cannot be instantiated private POP3Constants() { } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/pop3/POP3ConstructorTest.java000066400000000000000000000125401434047722200333210ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.pop3; import java.io.Reader; import junit.framework.TestCase; /** * The POP3* tests all presume the existence of the following parameters: mailserver: localhost (running on the default port 110) account: username=test; * password=password account: username=alwaysempty; password=password. mail: At least four emails in the test account and zero emails in the alwaysempty account * * If this won't work for you, you can change these parameters in the TestSetupParameters class. * * The tests were originally run on a default installation of James. Your mileage may vary based on the POP3 server you run the tests against. Some servers are * more standards-compliant than others. */ public class POP3ConstructorTest extends TestCase { String user = POP3Constants.user; String emptyUser = POP3Constants.emptyuser; String password = POP3Constants.password; String mailhost = POP3Constants.mailhost; public POP3ConstructorTest(final String name) { super(name); } /* * This test will ensure that the constants are not inadvertently changed. If the constants are changed in org.apache.commons.net.pop3 for some reason, this * test will have to be updated. */ public void testConstants() { // From POP3 assertEquals(110, POP3.DEFAULT_PORT); assertEquals(-1, POP3.DISCONNECTED_STATE); assertEquals(0, POP3.AUTHORIZATION_STATE); assertEquals(1, POP3.TRANSACTION_STATE); assertEquals(2, POP3.UPDATE_STATE); // From POP3Command assertEquals(0, POP3Command.USER); assertEquals(1, POP3Command.PASS); assertEquals(2, POP3Command.QUIT); assertEquals(3, POP3Command.STAT); assertEquals(4, POP3Command.LIST); assertEquals(5, POP3Command.RETR); assertEquals(6, POP3Command.DELE); assertEquals(7, POP3Command.NOOP); assertEquals(8, POP3Command.RSET); assertEquals(9, POP3Command.APOP); assertEquals(10, POP3Command.TOP); assertEquals(11, POP3Command.UIDL); } public void testPOP3ClientStateTransition() throws Exception { final POP3Client pop = new POP3Client(); // Initial state assertEquals(110, pop.getDefaultPort()); assertEquals(POP3.DISCONNECTED_STATE, pop.getState()); assertNull(pop.reader); assertNotNull(pop.replyLines); // Now connect pop.connect(mailhost); assertEquals(POP3.AUTHORIZATION_STATE, pop.getState()); // Now authenticate pop.login(user, password); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); // Now do a series of commands and make sure the state stays as it should pop.noop(); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); pop.status(); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); // Make sure we have at least one message to test final POP3MessageInfo[] msg = pop.listMessages(); if (msg.length > 0) { pop.deleteMessage(1); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); pop.reset(); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); pop.listMessage(1); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); pop.listMessages(); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); pop.listUniqueIdentifier(1); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); pop.listUniqueIdentifiers(); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); Reader r = pop.retrieveMessage(1); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); // Add some sleep here to handle network latency while (!r.ready()) { Thread.sleep(10); } r.close(); r = null; r = pop.retrieveMessageTop(1, 10); assertEquals(POP3.TRANSACTION_STATE, pop.getState()); // Add some sleep here to handle network latency while (!r.ready()) { Thread.sleep(10); } r.close(); r = null; } // Now logout pop.logout(); assertEquals(POP3.UPDATE_STATE, pop.getState()); } public void testPOP3DefaultConstructor() { final POP3 pop = new POP3(); assertEquals(110, pop.getDefaultPort()); assertEquals(POP3.DISCONNECTED_STATE, pop.getState()); assertNull(pop.reader); assertNotNull(pop.replyLines); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/smtp/000077500000000000000000000000001434047722200267275ustar00rootroot00000000000000SimpleSMTPHeaderTestCase.java000066400000000000000000000117521434047722200342230ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/smtp/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.smtp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assertions.assertThrows; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.Before; import org.junit.Test; public class SimpleSMTPHeaderTestCase { private SimpleSMTPHeader header; private Date beforeDate; // Returns the msg without a date private String checkDate(final String msg) { final Pattern pat = Pattern.compile("^(Date: (.+))$", Pattern.MULTILINE); final Matcher m = pat.matcher(msg); if (m.find()) { final String date = m.group(2); final String pattern = "EEE, dd MMM yyyy HH:mm:ss Z"; // Fri, 21 Nov 1997 09:55:06 -0600 final SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.ENGLISH); try { final Date sentDate = format.parse(date); // Round to nearest second because the text format does not include ms final long sentSecs = sentDate.getTime() / 1000; final long beforeDateSecs = beforeDate.getTime() / 1000; final Date afterDate = new Date(); final long afterDateSecs = afterDate.getTime() / 1000; if (sentSecs < beforeDateSecs) { fail(sentDate + " should be after " + beforeDate); } if (sentSecs > afterDateSecs) { fail(sentDate + " should be before " + afterDate); } } catch (final ParseException e) { fail("" + e); } final int start = m.start(1); final int end = m.end(1); if (start == 0) { return msg.substring(end + 1); } return msg.substring(0, start) + msg.substring(end + 1); } fail("Expecting Date header in " + msg); return null; } @Before public void setUp() { beforeDate = new Date(); header = new SimpleSMTPHeader("from@here.invalid", "to@there.invalid", "Test email"); } @Test public void testToString() { assertNotNull(header); // Note that the DotTerminatedMessageWriter converts LF to CRLF assertEquals("From: from@here.invalid\nTo: to@there.invalid\nSubject: Test email\n\n", checkDate(header.toString())); } @Test public void testToStringAddHeader() { final SimpleSMTPHeader hdr = new SimpleSMTPHeader("from@here.invalid", null, null); assertNotNull(hdr); hdr.addHeaderField("X-Header1", "value 1"); hdr.addHeaderField("X-Header2", "value 2"); // Note that the DotTerminatedMessageWriter converts LF to CRLF assertEquals("X-Header1: value 1\nX-Header2: value 2\nFrom: from@here.invalid\n\n", checkDate(hdr.toString())); } @Test public void testToStringAddHeaderDate() { final SimpleSMTPHeader hdr = new SimpleSMTPHeader("from@here.invalid", null, null); assertNotNull(hdr); hdr.addHeaderField("Date", "dummy date"); // does not replace the Date field assertEquals("Date: dummy date\nFrom: from@here.invalid\n\n", hdr.toString()); } @Test public void testToStringNoFrom() { assertThrows(IllegalArgumentException.class, () -> new SimpleSMTPHeader(null, null, null)); } @Test public void testToStringNoSubject() { final SimpleSMTPHeader hdr = new SimpleSMTPHeader("from@here.invalid", "to@there.invalid", null); assertNotNull(hdr); // Note that the DotTerminatedMessageWriter converts LF to CRLF assertEquals("From: from@here.invalid\nTo: to@there.invalid\n\n", checkDate(hdr.toString())); } @Test public void testToStringNoTo() { final SimpleSMTPHeader hdr = new SimpleSMTPHeader("from@here.invalid", null, null); assertNotNull(hdr); // Note that the DotTerminatedMessageWriter converts LF to CRLF assertEquals("From: from@here.invalid\n\n", checkDate(hdr.toString())); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/000077500000000000000000000000001434047722200272375ustar00rootroot00000000000000EchoOptionHandlerTest.java000066400000000000000000000042351434047722200342340ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * JUnit test class for EchoOptionHandler */ public class EchoOptionHandlerTest extends TelnetOptionHandlerTestAbstract { /** * setUp for the test. */ @Override protected void setUp() { opthand1 = new EchoOptionHandler(); opthand2 = new EchoOptionHandler(true, true, true, true); opthand3 = new EchoOptionHandler(false, false, false, false); } /** * test of server-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testAnswerSubnegotiation() { final int subn[] = { TelnetCommand.IAC, TelnetCommand.SB, TelnetOption.ECHO, 1, TelnetCommand.IAC, TelnetCommand.SE, }; final int resp1[] = opthand1.answerSubnegotiation(subn, subn.length); assertNull(resp1); } /** * test of the constructors. */ @Override public void testConstructors() { assertEquals(opthand1.getOptionCode(), TelnetOption.ECHO); super.testConstructors(); } /** * test of client-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testStartSubnegotiation() { final int resp1[] = opthand1.startSubnegotiationLocal(); final int resp2[] = opthand1.startSubnegotiationRemote(); assertNull(resp1); assertNull(resp2); } } InvalidTelnetOptionExceptionTest.java000066400000000000000000000027471434047722200365070ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import junit.framework.TestCase; /** * JUnit test class for InvalidTelnetOptionException */ public class InvalidTelnetOptionExceptionTest extends TestCase { private InvalidTelnetOptionException exc1; private String msg1; private int code1; /** * setUp for the test. */ @Override protected void setUp() { msg1 = "MSG"; code1 = 13; exc1 = new InvalidTelnetOptionException(msg1, code1); } /** * test of the constructors. */ public void testConstructors() { assertTrue(exc1.getMessage().indexOf(msg1) >= 0); assertTrue(exc1.getMessage().indexOf("" + code1) >= 0); } }SimpleOptionHandlerTest.java000066400000000000000000000043661434047722200346140ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * JUnit test class for SimpleOptionHandler */ public class SimpleOptionHandlerTest extends TelnetOptionHandlerTestAbstract { /** * setUp for the test. */ @Override protected void setUp() { opthand1 = new SimpleOptionHandler(4); opthand2 = new SimpleOptionHandler(8, true, true, true, true); opthand3 = new SimpleOptionHandler(91, false, false, false, false); } /** * test of server-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testAnswerSubnegotiation() { final int subn[] = { TelnetCommand.IAC, TelnetCommand.SB, 4, 1, TelnetCommand.IAC, TelnetCommand.SE, }; final int resp1[] = opthand1.answerSubnegotiation(subn, subn.length); assertNull(resp1); } /** * test of the constructors. */ @Override public void testConstructors() { assertEquals(opthand1.getOptionCode(), 4); assertEquals(opthand2.getOptionCode(), 8); assertEquals(opthand3.getOptionCode(), 91); super.testConstructors(); } /** * test of client-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testStartSubnegotiation() { final int resp1[] = opthand1.startSubnegotiationLocal(); final int resp2[] = opthand1.startSubnegotiationRemote(); assertNull(resp1); assertNull(resp2); } } SuppressGAOptionHandlerTest.java000066400000000000000000000043261434047722200354130ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * JUnit test class for SuppressGAOptionHandler */ public class SuppressGAOptionHandlerTest extends TelnetOptionHandlerTestAbstract { /** * setUp for the test. */ @Override protected void setUp() { opthand1 = new SuppressGAOptionHandler(); opthand2 = new SuppressGAOptionHandler(true, true, true, true); opthand3 = new SuppressGAOptionHandler(false, false, false, false); } /** * test of server-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testAnswerSubnegotiation() { final int subn[] = { TelnetCommand.IAC, TelnetCommand.SB, TelnetOption.SUPPRESS_GO_AHEAD, 1, TelnetCommand.IAC, TelnetCommand.SE, }; final int resp1[] = opthand1.answerSubnegotiation(subn, subn.length); assertNull(resp1); } /** * test of the constructors. */ @Override public void testConstructors() { assertEquals(opthand1.getOptionCode(), TelnetOption.SUPPRESS_GO_AHEAD); super.testConstructors(); } /** * test of client-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testStartSubnegotiation() { final int resp1[] = opthand1.startSubnegotiationLocal(); final int resp2[] = opthand1.startSubnegotiationRemote(); assertNull(resp1); assertNull(resp2); } } TelnetClientFunctionalTest.java000066400000000000000000000062631434047722200353070ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.InputStream; import java.io.OutputStream; import junit.framework.TestCase; /** * JUnit functional test for TelnetClient. Connects to the weather forecast service rainmaker.wunderground.com and asks for Los Angeles forecast. */ public class TelnetClientFunctionalTest extends TestCase { protected TelnetClient tc1; /** * test setUp */ @Override protected void setUp() { tc1 = new TelnetClient(); } /* * Do the functional test: - connect to the weather service - press return on the first menu - send LAX on the second menu - send X to exit */ public void testFunctionalTest() throws Exception { boolean testresult = false; tc1.connect("rainmaker.wunderground.com", 3000); try (final InputStream is = tc1.getInputStream(); final OutputStream os = tc1.getOutputStream()) { boolean cont = waitForString(is, "Return to continue:", 30000); if (cont) { os.write("\n".getBytes()); os.flush(); cont = waitForString(is, "city code--", 30000); } if (cont) { os.write("LAX\n".getBytes()); os.flush(); cont = waitForString(is, "Los Angeles", 30000); } if (cont) { cont = waitForString(is, "X to exit:", 30000); } if (cont) { os.write("X\n".getBytes()); os.flush(); tc1.disconnect(); testresult = true; } assertTrue(testresult); } } /* * Helper method. waits for a string with timeout */ public boolean waitForString(final InputStream is, final String end, final long timeout) throws Exception { final byte buffer[] = new byte[32]; final long starttime = System.currentTimeMillis(); String readbytes = ""; while ((readbytes.indexOf(end) < 0) && ((System.currentTimeMillis() - starttime) < timeout)) { if (is.available() > 0) { final int ret_read = is.read(buffer); readbytes = readbytes + new String(buffer, 0, ret_read); } else { Thread.sleep(500); } } if (readbytes.indexOf(end) >= 0) { return (true); } return (false); } }commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/TelnetClientTest.java000066400000000000000000000720411434047722200333400ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import junit.framework.TestCase; /** * JUnit test class for TelnetClient.s Implements protocol compliance tests */ public class TelnetClientTest extends TestCase implements TelnetNotificationHandler { /** * Handy holder to hold both sides of the connection used in testing for clarity. */ private class TestConnection { private final TelnetTestSimpleServer server; private final TelnetClient client; private final int port; TestConnection(final TelnetTestSimpleServer server, final TelnetClient client, final int port) { this.server = server; this.client = client; this.port = port; } protected void close() { TelnetClientTest.this.closeConnection(this.server, this.client, this.port); } } // four connections with different properties // to use in tests. private TestConnection STANDARD; private TestConnection OPTIONS; private TestConnection ANSI; private TestConnection NOREAD; private TestConnection SMALL_BUFFER; private final int NUM_CONNECTIONS = 5; protected int numdo; protected int numdont; protected int numwill; protected int numwont; protected int[] lastSubnegotiation; protected int lastSubnegotiationLength; void closeConnection(final TelnetTestSimpleServer server, final TelnetClient client, final int port) { if (server != null) { server.disconnect(); server.stop(); } try { if (client != null) { client.disconnect(); } } catch (final IOException e) { System.err.println("failed to close client-server connection on port " + port); System.err.println("ERROR in closeConnection(), " + e.getMessage()); } } /* * Helper method. compares two arrays of int */ protected boolean equalBytes(final byte a1[], final byte a2[]) { if (a1.length != a2.length) { return false; } boolean result = true; for (int ii = 0; ii < a1.length; ii++) { if (a1[ii] != a2[ii]) { result = false; } } return result; } /* * Callback method called when TelnetClient receives an option negotiation command.

* * @param negotiation_code - type of negotiation command received (RECEIVED_DO, RECEIVED_DONT, RECEIVED_WILL, RECEIVED_WONT)

* * @param option_code - code of the option negotiated

*/ @Override public void receivedNegotiation(final int negotiation_code, final int option_code) { switch (negotiation_code) { case TelnetNotificationHandler.RECEIVED_DO: numdo++; break; case TelnetNotificationHandler.RECEIVED_DONT: numdont++; break; case TelnetNotificationHandler.RECEIVED_WILL: numwill++; break; case TelnetNotificationHandler.RECEIVED_WONT: numwont++; break; default: break; } } /* * open connections needed for the tests for the test. */ @Override protected void setUp() throws Exception { final SimpleOptionHandler subnegotiationSizeHandler = new SimpleOptionHandler(99, false, false, true, false) { @Override public int[] answerSubnegotiation(final int[] suboptionData, final int suboptionLength) { lastSubnegotiation = suboptionData; lastSubnegotiationLength = suboptionLength; return null; } }; int socket = 0; super.setUp(); for (int port = 3333; socket < NUM_CONNECTIONS && port < 4000; port++) { TelnetTestSimpleServer server = null; TelnetClient client = null; try { server = new TelnetTestSimpleServer(port); switch (socket) { case 0: client = new TelnetClient(); // redundant but makes code clearer. client.setReaderThread(true); client.addOptionHandler(subnegotiationSizeHandler); client.connect("127.0.0.1", port); STANDARD = new TestConnection(server, client, port); break; case 1: client = new TelnetClient(); final TerminalTypeOptionHandler ttopt = new TerminalTypeOptionHandler("VT100", false, false, true, false); final EchoOptionHandler echoopt = new EchoOptionHandler(true, false, true, false); final SuppressGAOptionHandler gaopt = new SuppressGAOptionHandler(true, true, true, true); client.addOptionHandler(ttopt); client.addOptionHandler(echoopt); client.addOptionHandler(gaopt); client.connect("127.0.0.1", port); OPTIONS = new TestConnection(server, client, port); break; case 2: client = new TelnetClient("ANSI"); client.connect("127.0.0.1", port); ANSI = new TestConnection(server, client, port); break; case 3: client = new TelnetClient(); client.setReaderThread(false); client.connect("127.0.0.1", port); NOREAD = new TestConnection(server, client, port); break; case 4: client = new TelnetClient(8); client.addOptionHandler(subnegotiationSizeHandler); client.connect("127.0.0.1", port); SMALL_BUFFER = new TestConnection(server, client, port); break; } // only increment socket number on success socket++; } catch (final IOException e) { closeConnection(server, client, port); } } if (socket < NUM_CONNECTIONS) { System.err.println("Only created " + socket + " clients; wanted " + NUM_CONNECTIONS); } Thread.sleep(1000); } /* * @throws Exception */ @Override protected void tearDown() throws Exception { NOREAD.close(); ANSI.close(); OPTIONS.close(); STANDARD.close(); SMALL_BUFFER.close(); try { Thread.sleep(1000); } catch (final InterruptedException ie) { // do nothing } super.tearDown(); } /* * test of AYT functionality */ public void testAYT() throws Exception { boolean ayt_true_ok = false; boolean ayt_false_ok = false; final byte AYT[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.AYT }; final byte response[] = { (byte) '[', (byte) 'Y', (byte) 'e', (byte) 's', (byte) ']' }; final String inputs[] = new String[1]; final String outputs[] = new String[1]; inputs[0] = new String(AYT); outputs[0] = new String(response); final OutputStream os = ANSI.server.getOutputStream(); final InputStream is = ANSI.server.getInputStream(); final TelnetTestResponder tr = new TelnetTestResponder(is, os, inputs, outputs, 30000); assertNotNull(tr); final boolean res1 = ANSI.client.sendAYT(2000); if (res1 == true) { ayt_true_ok = true; } Thread.sleep(1000); is.skip(is.available()); final boolean res2 = ANSI.client.sendAYT(2000); if (res2 == false) { ayt_false_ok = true; } assertTrue(ayt_true_ok); assertTrue(ayt_false_ok); } /* * protocol compliance test in case of option handler removal */ public void testDeleteOptionHandler() throws Exception { boolean remove_ok = false; boolean remove_invalid_ok1 = false; boolean remove_invalid_ok2 = false; final byte buffread[] = new byte[6]; final byte send[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.ECHO, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.SUPPRESS_GO_AHEAD, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte expected[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final InputStream is = OPTIONS.server.getInputStream(); final OutputStream os = OPTIONS.server.getOutputStream(); Thread.sleep(1000); is.skip(is.available()); os.write(send); os.flush(); Thread.sleep(1000); if (is.available() == 0) { OPTIONS.client.deleteOptionHandler(TelnetOption.SUPPRESS_GO_AHEAD); Thread.sleep(1000); if (is.available() == 6) { is.read(buffread); if (equalBytes(buffread, expected)) { remove_ok = true; } } } try { OPTIONS.client.deleteOptionHandler(TelnetOption.SUPPRESS_GO_AHEAD); } catch (final Exception e) { remove_invalid_ok1 = true; } try { OPTIONS.client.deleteOptionHandler(550); } catch (final Exception e) { remove_invalid_ok2 = true; } assertTrue(remove_ok); assertTrue(remove_invalid_ok1); assertTrue(remove_invalid_ok2); assertTrue(OPTIONS.client.getLocalOptionState(TelnetOption.ECHO)); assertFalse(OPTIONS.client.getLocalOptionState(TelnetOption.SUPPRESS_GO_AHEAD)); assertFalse(OPTIONS.client.getLocalOptionState(TelnetOption.SUPPRESS_GO_AHEAD)); } /* * tests the initial condition of the sessions */ public void testInitial() throws Exception { boolean connect1_ok = false; boolean connect2_ok = false; boolean connect3_ok = false; boolean init2_ok = false; boolean add_invalid_ok1 = false; boolean add_invalid_ok2 = false; final byte buffread2[] = new byte[9]; final byte expected2[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) TelnetOption.ECHO, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) TelnetOption.SUPPRESS_GO_AHEAD, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.SUPPRESS_GO_AHEAD, }; final SimpleOptionHandler hand = new SimpleOptionHandler(550); try { STANDARD.client.addOptionHandler(hand); } catch (final Exception e) { add_invalid_ok1 = true; } try { OPTIONS.client.addOptionHandler(hand); } catch (final Exception e) { add_invalid_ok2 = true; } final InputStream is1 = STANDARD.server.getInputStream(); Thread.sleep(1000); if (is1.available() == 0) { connect1_ok = true; } Thread.sleep(1000); final InputStream is2 = OPTIONS.server.getInputStream(); if (is2.available() == 9) { is2.read(buffread2); connect2_ok = true; if (equalBytes(buffread2, expected2)) { init2_ok = true; } } final InputStream is3 = ANSI.server.getInputStream(); Thread.sleep(1000); if (is3.available() == 0) { connect3_ok = true; } assertTrue(connect1_ok); assertTrue(connect2_ok); assertTrue(connect3_ok); assertFalse(STANDARD.client.getLocalOptionState(TelnetOption.ECHO)); assertFalse(STANDARD.client.getRemoteOptionState(TelnetOption.ECHO)); assertFalse(OPTIONS.client.getLocalOptionState(TelnetOption.ECHO)); assertFalse(OPTIONS.client.getRemoteOptionState(TelnetOption.ECHO)); assertFalse(ANSI.client.getLocalOptionState(TelnetOption.TERMINAL_TYPE)); assertFalse(ANSI.client.getRemoteOptionState(TelnetOption.TERMINAL_TYPE)); assertTrue(init2_ok); assertTrue(add_invalid_ok1); assertTrue(add_invalid_ok2); } /* * test of max subnegotiation length */ public void testMaxSubnegotiationLength() throws Exception { final byte send[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB, (byte) 99, (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6, (byte) 7, (byte) 8, (byte) 9, (byte) 10, (byte) 11, (byte) 12, (byte) 13, (byte) 14, (byte) 15, (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE, }; final OutputStream os1 = SMALL_BUFFER.server.getOutputStream(); os1.write(send); os1.flush(); Thread.sleep(500); // we sent 16 bytes, but the buffer size should just be 8 assertEquals(8, lastSubnegotiationLength); assertEquals(8, lastSubnegotiation.length); assertEquals(99, lastSubnegotiation[0]); assertEquals(1, lastSubnegotiation[1]); assertEquals(2, lastSubnegotiation[2]); assertEquals(3, lastSubnegotiation[3]); assertEquals(4, lastSubnegotiation[4]); assertEquals(5, lastSubnegotiation[5]); assertEquals(6, lastSubnegotiation[6]); assertEquals(7, lastSubnegotiation[7]); final OutputStream os2 = STANDARD.server.getOutputStream(); os2.write(send); os2.flush(); Thread.sleep(500); // the standard subnegotiation buffer size is 512 assertEquals(16, lastSubnegotiationLength); assertEquals(512, lastSubnegotiation.length); assertEquals(99, lastSubnegotiation[0]); assertEquals(1, lastSubnegotiation[1]); assertEquals(2, lastSubnegotiation[2]); assertEquals(3, lastSubnegotiation[3]); assertEquals(4, lastSubnegotiation[4]); assertEquals(5, lastSubnegotiation[5]); assertEquals(6, lastSubnegotiation[6]); assertEquals(7, lastSubnegotiation[7]); } /* * test of option negotiation notification */ public void testNotification() throws Exception { final byte buffread1[] = new byte[6]; final byte send1[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) 15, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) 15, }; final byte buffread2[] = new byte[9]; final byte send2[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.TERMINAL_TYPE, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) TelnetOption.ECHO, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.SUPPRESS_GO_AHEAD, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte buffread2b[] = new byte[11]; numdo = 0; numdont = 0; numwill = 0; numwont = 0; OPTIONS.client.registerNotifHandler(this); final InputStream is1 = STANDARD.server.getInputStream(); final OutputStream os1 = STANDARD.server.getOutputStream(); is1.skip(is1.available()); os1.write(send1); os1.flush(); Thread.sleep(500); if (is1.available() > 0) { is1.read(buffread1); } final InputStream is2 = OPTIONS.server.getInputStream(); final OutputStream os2 = OPTIONS.server.getOutputStream(); Thread.sleep(500); is2.skip(is2.available()); os2.write(send2); os2.flush(); Thread.sleep(500); if (is2.available() > 0) { is2.read(buffread2); Thread.sleep(1000); if (is2.available() > 0) { is2.read(buffread2b); } } assertEquals(2, numdo); assertEquals(1, numdont); assertEquals(1, numwont); assertEquals(0, numwill); } /* * protocol compliance test for option negotiation */ public void testOptionNegotiation() throws Exception { boolean negotiation1_ok = false; final byte buffread1[] = new byte[6]; final byte send1[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) 15, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) 15, }; final byte expected1[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) 15, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) 15, }; boolean negotiation2_ok = false; final byte buffread2[] = new byte[9]; final byte send2[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.TERMINAL_TYPE, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) TelnetOption.ECHO, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.SUPPRESS_GO_AHEAD, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte expected2[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) TelnetOption.TERMINAL_TYPE, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.ECHO, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte buffread2b[] = new byte[11]; final byte send2b[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB, (byte) TelnetOption.TERMINAL_TYPE, (byte) 1, (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE, }; final byte expected2b[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB, (byte) TelnetOption.TERMINAL_TYPE, (byte) 0, (byte) 'V', (byte) 'T', (byte) '1', (byte) '0', (byte) '0', (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE, }; boolean negotiation3_ok = false; final byte buffread3[] = new byte[6]; final byte send3[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.TERMINAL_TYPE, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte expected3[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) TelnetOption.TERMINAL_TYPE, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte buffread3b[] = new byte[10]; final byte send3b[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB, (byte) TelnetOption.TERMINAL_TYPE, (byte) 1, (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE, }; final byte expected3b[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB, (byte) TelnetOption.TERMINAL_TYPE, (byte) 0, (byte) 'A', (byte) 'N', (byte) 'S', (byte) 'I', (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE, }; final InputStream is1 = STANDARD.server.getInputStream(); final OutputStream os1 = STANDARD.server.getOutputStream(); is1.skip(is1.available()); os1.write(send1); os1.flush(); Thread.sleep(1000); if (is1.available() == 6) { is1.read(buffread1); if (equalBytes(buffread1, expected1)) { negotiation1_ok = true; } } final InputStream is2 = OPTIONS.server.getInputStream(); final OutputStream os2 = OPTIONS.server.getOutputStream(); Thread.sleep(1000); is2.skip(is2.available()); os2.write(send2); os2.flush(); Thread.sleep(1000); if (is2.available() == 9) { is2.read(buffread2); if (equalBytes(buffread2, expected2)) { negotiation2_ok = true; } if (negotiation2_ok) { negotiation2_ok = false; os2.write(send2b); os2.flush(); Thread.sleep(1000); if (is2.available() == 11) { is2.read(buffread2b); if (equalBytes(buffread2b, expected2b)) { negotiation2_ok = true; } } } } final InputStream is3 = ANSI.server.getInputStream(); final OutputStream os3 = ANSI.server.getOutputStream(); Thread.sleep(1000); is3.skip(is3.available()); os3.write(send3); os3.flush(); Thread.sleep(1000); if (is3.available() == 6) { is3.read(buffread3); if (equalBytes(buffread3, expected3)) { negotiation3_ok = true; } if (negotiation3_ok) { negotiation3_ok = false; os3.write(send3b); os3.flush(); Thread.sleep(1000); if (is3.available() == 10) { is3.read(buffread3b); if (equalBytes(buffread3b, expected3b)) { negotiation3_ok = true; } } } } assertTrue(negotiation1_ok); assertTrue(negotiation2_ok); assertTrue(negotiation3_ok); assertFalse(STANDARD.client.getLocalOptionState(15)); assertFalse(STANDARD.client.getRemoteOptionState(15)); assertFalse(STANDARD.client.getLocalOptionState(TelnetOption.TERMINAL_TYPE)); assertFalse(OPTIONS.client.getLocalOptionState(TelnetOption.ECHO)); assertFalse(OPTIONS.client.getRemoteOptionState(TelnetOption.ECHO)); assertTrue(OPTIONS.client.getLocalOptionState(TelnetOption.SUPPRESS_GO_AHEAD)); assertFalse(OPTIONS.client.getRemoteOptionState(TelnetOption.SUPPRESS_GO_AHEAD)); assertTrue(OPTIONS.client.getLocalOptionState(TelnetOption.TERMINAL_TYPE)); assertTrue(ANSI.client.getLocalOptionState(TelnetOption.TERMINAL_TYPE)); assertFalse(OPTIONS.client.getLocalOptionState(TelnetOption.ECHO)); } /* * protocol compliance test for option renegotiation */ public void testOptionRenegotiation() throws Exception { boolean negotiation1_ok = false; final byte buffread[] = new byte[6]; final byte send[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) TelnetOption.ECHO, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte expected[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) TelnetOption.SUPPRESS_GO_AHEAD }; final byte buffread2[] = new byte[3]; final byte send2[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) TelnetOption.ECHO, }; final byte expected2[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) TelnetOption.ECHO, }; final InputStream is = OPTIONS.server.getInputStream(); final OutputStream os = OPTIONS.server.getOutputStream(); Thread.sleep(1000); is.skip(is.available()); os.write(send); os.flush(); Thread.sleep(1000); if (is.available() == 6) { is.read(buffread); if (equalBytes(buffread, expected)) { negotiation1_ok = true; } if (negotiation1_ok) { negotiation1_ok = false; os.write(send2); os.flush(); Thread.sleep(1000); if (is.available() == 3) { is.read(buffread2); if (equalBytes(buffread2, expected2)) { negotiation1_ok = true; } } } } assertTrue(negotiation1_ok); assertFalse(OPTIONS.client.getLocalOptionState(TelnetOption.ECHO)); } /* * test of setReaderThread */ public void testSetReaderThread() throws Exception { boolean negotiation1_ok = false; boolean negotiation2_ok = false; boolean read_ok = false; final byte buffread1[] = new byte[6]; final byte send1[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO, (byte) 15, (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL, (byte) 15, }; final byte expected1[] = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT, (byte) 15, (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT, (byte) 15, }; final InputStream is1 = NOREAD.server.getInputStream(); final OutputStream os1 = NOREAD.server.getOutputStream(); is1.skip(is1.available()); os1.write(send1); os1.flush(); os1.write("A".getBytes()); os1.flush(); Thread.sleep(1000); final InputStream instr = NOREAD.client.getInputStream(); final byte[] buff = new byte[4]; final int ret_read = instr.read(buff); if (ret_read == 1 && buff[0] == 'A') { read_ok = true; } // if(is1.available() == 6) // { int read = 0; int pos = 0; byte[] tmp = new byte[16]; while (pos < 5) { read = is1.read(tmp); System.arraycopy(tmp, 0, buffread1, pos, read); pos += read; } if (equalBytes(buffread1, expected1)) { negotiation1_ok = true; // } } final InputStream is2 = STANDARD.server.getInputStream(); final OutputStream os2 = STANDARD.server.getOutputStream(); Thread.sleep(1000); is2.skip(is2.available()); os2.write(send1); os2.flush(); Thread.sleep(1000); tmp = new byte[16]; while (pos < 5) { read = is2.read(tmp); System.arraycopy(tmp, 0, buffread1, pos, read); pos += read; } // if(is2.available() == 6) // { is2.read(buffread1); if (equalBytes(buffread1, expected1)) { negotiation2_ok = true; // } } assertFalse(NOREAD.client.getReaderThread()); assertTrue(STANDARD.client.getReaderThread()); assertTrue("Expected read_ok to be true, got " + read_ok, read_ok); assertTrue("Expected negotiation1_ok to be true, got " + negotiation1_ok, negotiation1_ok); assertTrue("Expected negotiation2_ok to be true, got " + negotiation2_ok, negotiation2_ok); } /* * test of Spy functionality */ public void testSpy() throws Exception { boolean test1spy_ok = false; boolean test2spy_ok = false; boolean stopspy_ok = false; final byte expected1[] = { (byte) 't', (byte) 'e', (byte) 's', (byte) 't', (byte) '1' }; final byte expected2[] = { (byte) 't', (byte) 'e', (byte) 's', (byte) 't', (byte) '2' }; try (final PipedOutputStream po = new PipedOutputStream(); final PipedInputStream pi = new PipedInputStream(po)) { final OutputStream os = STANDARD.server.getOutputStream(); final OutputStream ostc = STANDARD.client.getOutputStream(); STANDARD.client.registerSpyStream(po); os.write("test1".getBytes()); os.flush(); Thread.sleep(1000); final byte buffer[] = new byte[5]; if (pi.available() == 5) { pi.read(buffer); if (equalBytes(buffer, expected1)) { test1spy_ok = true; } } ostc.write("test2".getBytes()); ostc.flush(); Thread.sleep(1000); if (pi.available() == 5) { pi.read(buffer); if (equalBytes(buffer, expected2)) { test2spy_ok = true; } } STANDARD.client.stopSpyStream(); os.write("test1".getBytes()); os.flush(); ostc.write("test2".getBytes()); ostc.flush(); Thread.sleep(1000); if (pi.available() == 0) { stopspy_ok = true; } assertTrue(test1spy_ok); assertTrue(test2spy_ok); assertTrue(stopspy_ok); } } }TelnetOptionHandlerTestAbstract.java000066400000000000000000000064311434047722200362750ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import junit.framework.TestCase; /** * The TelnetOptionHandlerTest is the abstract class for testing TelnetOptionHandler. It can be used to derive the actual test classes for TelnetOptionHadler * derived classes, by adding creation of three new option handlers and testing of the specific subnegotiation behavior. */ public abstract class TelnetOptionHandlerTestAbstract extends TestCase { TelnetOptionHandler opthand1; TelnetOptionHandler opthand2; TelnetOptionHandler opthand3; /** * setUp for the test. The derived test class must implement this method by creating opthand1, opthand2, opthand3 like in the following: opthand1 = new * EchoOptionHandler(); opthand2 = new EchoOptionHandler(true, true, true, true); opthand3 = new EchoOptionHandler(false, false, false, false); */ @Override protected abstract void setUp(); /** * test of server-driven subnegotiation. Abstract test: the derived class should implement it. */ public abstract void testAnswerSubnegotiation(); // test subnegotiation /** * test of the constructors. The derived class may add test of the option code. */ public void testConstructors() { // add test of the option code assertFalse(opthand1.getInitLocal()); assertFalse(opthand1.getInitRemote()); assertFalse(opthand1.getAcceptLocal()); assertFalse(opthand1.getAcceptRemote()); assertTrue(opthand2.getInitLocal()); assertTrue(opthand2.getInitRemote()); assertTrue(opthand2.getAcceptLocal()); assertTrue(opthand2.getAcceptRemote()); assertFalse(opthand3.getInitLocal()); assertFalse(opthand3.getInitRemote()); assertFalse(opthand3.getAcceptLocal()); assertFalse(opthand3.getAcceptRemote()); } /** * test of setDo/getDo */ public void testDo() { opthand2.setDo(true); opthand3.setDo(false); assertFalse(opthand1.getDo()); assertTrue(opthand2.getDo()); assertFalse(opthand3.getDo()); } /** * test of client-driven subnegotiation. Abstract test: the derived class should implement it. */ public abstract void testStartSubnegotiation(); /** * test of setWill/getWill */ public void testWill() { opthand2.setWill(true); opthand3.setWill(false); assertFalse(opthand1.getWill()); assertTrue(opthand2.getWill()); assertFalse(opthand3.getWill()); } }commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/TelnetOptionTest.java000066400000000000000000000030501434047722200333640ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import junit.framework.TestCase; /** * JUnit test class for TelnetOption */ public class TelnetOptionTest extends TestCase { /** * test of the getOption method. */ public void testGetOption() { assertEquals(TelnetOption.getOption(0), "BINARY"); assertEquals(TelnetOption.getOption(91), "UNASSIGNED"); assertEquals(TelnetOption.getOption(255), "Extended-Options-List"); } /** * test of the isValidOption method. */ public void testisValidOption() { assertTrue(TelnetOption.isValidOption(0)); assertTrue(TelnetOption.isValidOption(91)); assertTrue(TelnetOption.isValidOption(255)); assertFalse(TelnetOption.isValidOption(256)); } }TelnetTestResponder.java000066400000000000000000000057341434047722200340110ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.InputStream; import java.io.OutputStream; /** * Simple stream responder. Waits for strings on an input stream and answers sending corresponfing strings on an output stream. The reader runs in a separate * thread. */ public class TelnetTestResponder implements Runnable { InputStream _is; OutputStream _os; String _inputs[], _outputs[]; long _timeout; /** * Constructor. Starts a new thread for the reader. *

* * @param is - InputStream on which to read. * @param os - OutputStream on which to answer. * @param inputs - Array of waited for Strings. * @param outputs - Array of answers. * @param timeout - milliseconds */ public TelnetTestResponder(final InputStream is, final OutputStream os, final String inputs[], final String outputs[], final long timeout) { _is = is; _os = os; _timeout = timeout; _inputs = inputs; _outputs = outputs; final Thread reader = new Thread(this); reader.start(); } /** * Runs the responder */ @Override public void run() { boolean result = false; final byte buffer[] = new byte[32]; final long starttime = System.currentTimeMillis(); try { final StringBuilder readbytes = new StringBuilder(); while (!result && System.currentTimeMillis() - starttime < _timeout) { if (_is.available() > 0) { final int ret_read = _is.read(buffer); readbytes.append(new String(buffer, 0, ret_read)); for (int ii = 0; ii < _inputs.length; ii++) { if (readbytes.indexOf(_inputs[ii]) >= 0) { Thread.sleep(1000 * ii); _os.write(_outputs[ii].getBytes()); result = true; } } } else { Thread.sleep(500); } } } catch (final Exception e) { System.err.println("Error while waiting endstring. " + e.getMessage()); } } } TelnetTestSimpleServer.java000066400000000000000000000066241434047722200344670ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * Simple TCP server. Waits for connections on a TCP port in a separate thread. */ public class TelnetTestSimpleServer implements Runnable { ServerSocket serverSocket; Socket clientSocket; Thread listener; /* * test of client-driven subnegotiation.

* * @param port - server port on which to listen. * * @throws IOException on error */ public TelnetTestSimpleServer(final int port) throws IOException { serverSocket = new ServerSocket(port); listener = new Thread(this); listener.start(); } public void disconnect() { if (clientSocket == null) { return; } synchronized (clientSocket) { try { clientSocket.notify(); } catch (final Exception e) { System.err.println("Exception in notify, " + e.getMessage()); } } } public InputStream getInputStream() throws IOException { if (clientSocket != null) { return clientSocket.getInputStream(); } return null; } public OutputStream getOutputStream() throws IOException { if (clientSocket != null) { return clientSocket.getOutputStream(); } return null; } @Override public void run() { boolean bError = false; while (!bError) { try { clientSocket = serverSocket.accept(); synchronized (clientSocket) { try { clientSocket.wait(); } catch (final Exception e) { System.err.println("Exception in wait, " + e.getMessage()); } try { clientSocket.close(); } catch (final Exception e) { System.err.println("Exception in close, " + e.getMessage()); } } } catch (final IOException e) { bError = true; } } try { serverSocket.close(); } catch (final Exception e) { System.err.println("Exception in close, " + e.getMessage()); } } public void stop() { listener.interrupt(); try { serverSocket.close(); } catch (final Exception e) { System.err.println("Exception in close, " + e.getMessage()); } } } TerminalTypeOptionHandlerTest.java000066400000000000000000000053331434047722200357730ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; public class TerminalTypeOptionHandlerTest extends TelnetOptionHandlerTestAbstract { /* * compares two arrays of int */ protected boolean equalInts(final int a1[], final int a2[]) { if (a1.length != a2.length) { return false; } boolean result = true; for (int ii = 0; ii < a1.length; ii++) { if (a1[ii] != a2[ii]) { result = false; } } return result; } @Override protected void setUp() { opthand1 = new TerminalTypeOptionHandler("VT100"); opthand2 = new TerminalTypeOptionHandler("ANSI", true, true, true, true); opthand3 = new TerminalTypeOptionHandler("ANSI", false, false, false, false); } /* * test of client-driven subnegotiation. Checks that the terminal type is sent */ @Override public void testAnswerSubnegotiation() { final int subn[] = { TelnetOption.TERMINAL_TYPE, 1 }; final int expected1[] = { TelnetOption.TERMINAL_TYPE, 0, 'V', 'T', '1', '0', '0' }; final int expected2[] = { TelnetOption.TERMINAL_TYPE, 0, 'A', 'N', 'S', 'I' }; final int resp1[] = opthand1.answerSubnegotiation(subn, subn.length); final int resp2[] = opthand2.answerSubnegotiation(subn, subn.length); assertTrue(equalInts(resp1, expected1)); assertTrue(equalInts(resp2, expected2)); } @Override public void testConstructors() { assertEquals(opthand1.getOptionCode(), TelnetOption.TERMINAL_TYPE); super.testConstructors(); } /* * test of client-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testStartSubnegotiation() { final int resp1[] = opthand1.startSubnegotiationLocal(); final int resp2[] = opthand1.startSubnegotiationRemote(); assertNull(resp1); assertNull(resp2); } } WindowSizeOptionHandlerTest.java000066400000000000000000000064621434047722200354640ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/telnet/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.telnet; /** * JUnit test class for TerminalTypeOptionHandler */ public class WindowSizeOptionHandlerTest extends TelnetOptionHandlerTestAbstract { /** * compares two arrays of int */ private void equalInts(final int a1[], final int a2[]) { assertEquals("Arrays should be the same length", a1.length, a2.length); for (int ii = 0; ii < a1.length; ii++) { assertEquals("Array entry " + ii + " should match", a1[ii], a2[ii]); } } /** * setUp for the test. */ @Override protected void setUp() { opthand1 = new WindowSizeOptionHandler(80, 24); opthand2 = new WindowSizeOptionHandler(255, 255, true, true, true, true); opthand3 = new WindowSizeOptionHandler(0xFFFF, 0x00FF, false, false, false, false); } /** * test of client-driven subnegotiation. Checks that nothing is sent */ @Override public void testAnswerSubnegotiation() { final int subn[] = { TelnetOption.WINDOW_SIZE, 24, 80 }; final int resp1[] = opthand1.answerSubnegotiation(subn, subn.length); final int resp2[] = opthand2.answerSubnegotiation(subn, subn.length); final int resp3[] = opthand3.answerSubnegotiation(subn, subn.length); assertNull(resp1); assertNull(resp2); assertNull(resp3); } /** * test of the constructors. */ @Override public void testConstructors() { assertEquals(TelnetOption.WINDOW_SIZE, opthand1.getOptionCode()); super.testConstructors(); } /** * test of client-driven subnegotiation. Checks that no subnegotiation is made. */ @Override public void testStartSubnegotiation() { assertNull(opthand1.startSubnegotiationRemote()); assertNull(opthand2.startSubnegotiationRemote()); assertNull(opthand3.startSubnegotiationRemote()); } /** * test of client-driven subnegotiation. * */ public void testStartSubnegotiationLocal() { final int[] exp1 = { 31, 0, 80, 0, 24 }; final int[] start1 = opthand1.startSubnegotiationLocal(); assertEquals(5, start1.length); equalInts(exp1, start1); final int[] exp2 = { 31, 0, 255, 255, 0, 255, 255 }; final int[] start2 = opthand2.startSubnegotiationLocal(); equalInts(exp2, start2); final int[] exp3 = { 31, 255, 255, 255, 255, 0, 255, 255 }; final int[] start3 = opthand3.startSubnegotiationLocal(); equalInts(exp3, start3); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/tftp/000077500000000000000000000000001434047722200267215ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/tftp/TFTPServer.java000066400000000000000000000763711434047722200315460ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; 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.io.PrintStream; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketTimeoutException; import java.util.Enumeration; import java.util.HashSet; import org.apache.commons.net.io.FromNetASCIIOutputStream; import org.apache.commons.net.io.ToNetASCIIInputStream; /** * A fully multi-threaded tftp server. Can handle multiple clients at the same time. Implements RFC 1350 and wrapping block numbers for large file support. * * To launch, just create an instance of the class. An IOException will be thrown if the server fails to start for reasons such as port in use, port denied, * etc. * * To stop, use the shutdown method. * * To check to see if the server is still running (or if it stopped because of an error), call the isRunning() method. * * By default, events are not logged to stdout/stderr. This can be changed with the setLog and setLogError methods. * *

* Example usage is below: * * * public static void main(String[] args) throws Exception * { * if (args.length != 1) * { * System.out * .println("You must provide 1 argument - the base path for the server to serve from."); * System.exit(1); * } * * TFTPServer ts = new TFTPServer(new File(args[0]), new File(args[0]), GET_AND_PUT); * ts.setSocketTimeout(2000); * * System.out.println("TFTP Server running. Press enter to stop."); * new InputStreamReader(System.in).read(); * * ts.shutdown(); * System.out.println("Server shut down."); * System.exit(0); * } * * * * @since 2.0 */ public class TFTPServer implements Runnable { public enum ServerMode { GET_ONLY, PUT_ONLY, GET_AND_PUT } /* * An instance of an ongoing transfer. */ private class TFTPTransfer implements Runnable { private final TFTPPacket tftpPacket_; private boolean shutdownTransfer; TFTP transferTftp_; public TFTPTransfer(final TFTPPacket tftpPacket) { tftpPacket_ = tftpPacket; } /* * Utility method to make sure that paths provided by tftp clients do not get outside of the serverRoot directory. */ private File buildSafeFile(final File serverDirectory, final String fileName, final boolean createSubDirs) throws IOException { File temp = new File(serverDirectory, fileName); temp = temp.getCanonicalFile(); if (!isSubdirectoryOf(serverDirectory, temp)) { throw new IOException("Cannot access files outside of tftp server root."); } // ensure directory exists (if requested) if (createSubDirs) { createDirectory(temp.getParentFile()); } return temp; } /* * recursively create subdirectories */ private void createDirectory(final File file) throws IOException { final File parent = file.getParentFile(); if (parent == null) { throw new IOException("Unexpected error creating requested directory"); } if (!parent.exists()) { // recurse... createDirectory(parent); } if (!parent.isDirectory()) { throw new IOException("Invalid directory path - file in the way of requested folder"); } if (file.isDirectory()) { return; } final boolean result = file.mkdir(); if (!result) { throw new IOException("Couldn't create requested directory"); } } /* * Handle a tftp read request. */ private void handleRead(final TFTPReadRequestPacket trrp) throws IOException, TFTPPacketException { InputStream is = null; try { if (mode_ == ServerMode.PUT_ONLY) { transferTftp_.bufferedSend( new TFTPErrorPacket(trrp.getAddress(), trrp.getPort(), TFTPErrorPacket.ILLEGAL_OPERATION, "Read not allowed by server.")); return; } try { is = new BufferedInputStream(new FileInputStream(buildSafeFile(serverReadDirectory_, trrp.getFilename(), false))); } catch (final FileNotFoundException e) { transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp.getPort(), TFTPErrorPacket.FILE_NOT_FOUND, e.getMessage())); return; } catch (final Exception e) { transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp.getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage())); return; } if (trrp.getMode() == TFTP.NETASCII_MODE) { is = new ToNetASCIIInputStream(is); } final byte[] temp = new byte[TFTPDataPacket.MAX_DATA_LENGTH]; TFTPPacket answer; int block = 1; boolean sendNext = true; int readLength = TFTPDataPacket.MAX_DATA_LENGTH; TFTPDataPacket lastSentData = null; // We are reading a file, so when we read less than the // requested bytes, we know that we are at the end of the file. while (readLength == TFTPDataPacket.MAX_DATA_LENGTH && !shutdownTransfer) { if (sendNext) { readLength = is.read(temp); if (readLength == -1) { readLength = 0; } lastSentData = new TFTPDataPacket(trrp.getAddress(), trrp.getPort(), block, temp, 0, readLength); sendData(transferTftp_, lastSentData); // send the data } answer = null; int timeoutCount = 0; while (!shutdownTransfer && (answer == null || !answer.getAddress().equals(trrp.getAddress()) || answer.getPort() != trrp.getPort())) { // listen for an answer. if (answer != null) { // The answer that we got didn't come from the // expected source, fire back an error, and continue // listening. log_.println("TFTP Server ignoring message from unexpected source."); transferTftp_.bufferedSend( new TFTPErrorPacket(answer.getAddress(), answer.getPort(), TFTPErrorPacket.UNKNOWN_TID, "Unexpected Host or Port")); } try { answer = transferTftp_.bufferedReceive(); } catch (final SocketTimeoutException e) { if (timeoutCount >= maxTimeoutRetries_) { throw e; } // didn't get an ack for this data. need to resend // it. timeoutCount++; transferTftp_.bufferedSend(lastSentData); continue; } } if (answer == null || !(answer instanceof TFTPAckPacket)) { if (!shutdownTransfer) { logError_.println("Unexpected response from tftp client during transfer (" + answer + "). Transfer aborted."); } break; } // once we get here, we know we have an answer packet // from the correct host. final TFTPAckPacket ack = (TFTPAckPacket) answer; if (ack.getBlockNumber() != block) { /* * The origional tftp spec would have called on us to resend the previous data here, however, that causes the SAS Syndrome. * http://www.faqs.org/rfcs/rfc1123.html section 4.2.3.1 The modified spec says that we ignore a duplicate ack. If the packet was really * lost, we will time out on receive, and resend the previous data at that point. */ sendNext = false; } else { // send the next block block++; if (block > 65535) { // wrap the block number block = 0; } sendNext = true; } } } finally { try { if (is != null) { is.close(); } } catch (final IOException e) { // noop } } } /* * handle a tftp write request. */ private void handleWrite(final TFTPWriteRequestPacket twrp) throws IOException, TFTPPacketException { OutputStream bos = null; try { if (mode_ == ServerMode.GET_ONLY) { transferTftp_.bufferedSend( new TFTPErrorPacket(twrp.getAddress(), twrp.getPort(), TFTPErrorPacket.ILLEGAL_OPERATION, "Write not allowed by server.")); return; } int lastBlock = 0; final String fileName = twrp.getFilename(); try { final File temp = buildSafeFile(serverWriteDirectory_, fileName, true); if (temp.exists()) { transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp.getPort(), TFTPErrorPacket.FILE_EXISTS, "File already exists")); return; } bos = new BufferedOutputStream(new FileOutputStream(temp)); if (twrp.getMode() == TFTP.NETASCII_MODE) { bos = new FromNetASCIIOutputStream(bos); } } catch (final Exception e) { transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp.getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage())); return; } TFTPAckPacket lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0); sendData(transferTftp_, lastSentAck); // send the data while (true) { // get the response - ensure it is from the right place. TFTPPacket dataPacket = null; int timeoutCount = 0; while (!shutdownTransfer && (dataPacket == null || !dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket.getPort() != twrp.getPort())) { // listen for an answer. if (dataPacket != null) { // The data that we got didn't come from the // expected source, fire back an error, and continue // listening. log_.println("TFTP Server ignoring message from unexpected source."); transferTftp_.bufferedSend( new TFTPErrorPacket(dataPacket.getAddress(), dataPacket.getPort(), TFTPErrorPacket.UNKNOWN_TID, "Unexpected Host or Port")); } try { dataPacket = transferTftp_.bufferedReceive(); } catch (final SocketTimeoutException e) { if (timeoutCount >= maxTimeoutRetries_) { throw e; } // It didn't get our ack. Resend it. transferTftp_.bufferedSend(lastSentAck); timeoutCount++; continue; } } if (dataPacket instanceof TFTPWriteRequestPacket) { // it must have missed our initial ack. Send another. lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0); transferTftp_.bufferedSend(lastSentAck); } else if (dataPacket == null || !(dataPacket instanceof TFTPDataPacket)) { if (!shutdownTransfer) { logError_.println("Unexpected response from tftp client during transfer (" + dataPacket + "). Transfer aborted."); } break; } else { final int block = ((TFTPDataPacket) dataPacket).getBlockNumber(); final byte[] data = ((TFTPDataPacket) dataPacket).getData(); final int dataLength = ((TFTPDataPacket) dataPacket).getDataLength(); final int dataOffset = ((TFTPDataPacket) dataPacket).getDataOffset(); if (block > lastBlock || lastBlock == 65535 && block == 0) { // it might resend a data block if it missed our ack // - don't rewrite the block. bos.write(data, dataOffset, dataLength); lastBlock = block; } lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), block); sendData(transferTftp_, lastSentAck); // send the data if (dataLength < TFTPDataPacket.MAX_DATA_LENGTH) { // end of stream signal - The tranfer is complete. bos.close(); // But my ack may be lost - so listen to see if I // need to resend the ack. for (int i = 0; i < maxTimeoutRetries_; i++) { try { dataPacket = transferTftp_.bufferedReceive(); } catch (final SocketTimeoutException e) { // this is the expected route - the client // shouldn't be sending any more packets. break; } if (dataPacket != null && (!dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket.getPort() != twrp.getPort())) { // make sure it was from the right client... transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket.getAddress(), dataPacket.getPort(), TFTPErrorPacket.UNKNOWN_TID, "Unexpected Host or Port")); } else { // This means they sent us the last // datapacket again, must have missed our // ack. resend it. transferTftp_.bufferedSend(lastSentAck); } } // all done. break; } } } } finally { if (bos != null) { bos.close(); } } } /* * recursively check to see if one directory is a parent of another. */ private boolean isSubdirectoryOf(final File parent, final File child) { final File childsParent = child.getParentFile(); if (childsParent == null) { return false; } if (childsParent.equals(parent)) { return true; } return isSubdirectoryOf(parent, childsParent); } @Override public void run() { try { transferTftp_ = newTFTP(); transferTftp_.beginBufferedOps(); transferTftp_.setDefaultTimeout(socketTimeout_); transferTftp_.open(); if (tftpPacket_ instanceof TFTPReadRequestPacket) { handleRead((TFTPReadRequestPacket) tftpPacket_); } else if (tftpPacket_ instanceof TFTPWriteRequestPacket) { handleWrite((TFTPWriteRequestPacket) tftpPacket_); } else { log_.println("Unsupported TFTP request (" + tftpPacket_ + ") - ignored."); } } catch (final Exception e) { if (!shutdownTransfer) { logError_.println("Unexpected Error in during TFTP file transfer. Transfer aborted. " + e); } } finally { try { if (transferTftp_ != null && transferTftp_.isOpen()) { transferTftp_.endBufferedOps(); transferTftp_.close(); } } catch (final Exception e) { // noop } synchronized (transfers_) { transfers_.remove(this); } } } public void shutdown() { shutdownTransfer = true; try { transferTftp_.close(); } catch (final RuntimeException e) { // noop } } } private static final int DEFAULT_TFTP_PORT = 69; /* /dev/null output stream (default) */ private static final PrintStream nullStream = new PrintStream(new OutputStream() { @Override public void write(final byte[] b) throws IOException { } @Override public void write(final int b) { } }); private final HashSet transfers_ = new HashSet<>(); private volatile boolean shutdownServer; private TFTP serverTftp_; private File serverReadDirectory_; private File serverWriteDirectory_; private final int port_; private final InetAddress laddr_; private Exception serverException; private final ServerMode mode_; // don't have access to a logger api, so we will log to these streams, which // by default are set to a no-op logger private PrintStream log_; private PrintStream logError_; private int maxTimeoutRetries_ = 3; private int socketTimeout_; private Thread serverThread; /** * Start a TFTP Server on the specified port. Gets and Puts occur in the specified directory. * * The server will start in another thread, allowing this constructor to return immediately. * * If a get or a put comes in with a relative path that tries to get outside of the serverDirectory, then the get or put will be denied. * * GET_ONLY mode only allows gets, PUT_ONLY mode only allows puts, and GET_AND_PUT allows both. Modes are defined as int constants in this class. * * @param serverReadDirectory directory for GET requests * @param serverWriteDirectory directory for PUT requests * @param port The local port to bind to. * @param localaddr The local address to bind to. * @param mode A value as specified above. * @param log Stream to write log message to. If not provided, uses System.out * @param errorLog Stream to write error messages to. If not provided, uses System.err. * @throws IOException if the server directory is invalid or does not exist. */ public TFTPServer(final File serverReadDirectory, final File serverWriteDirectory, final int port, final InetAddress localaddr, final ServerMode mode, final PrintStream log, final PrintStream errorLog) throws IOException { port_ = port; mode_ = mode; laddr_ = localaddr; log_ = log == null ? nullStream : log; logError_ = errorLog == null ? nullStream : errorLog; launch(serverReadDirectory, serverWriteDirectory); } /** * Start a TFTP Server on the specified port. Gets and Puts occur in the specified directory. * * The server will start in another thread, allowing this constructor to return immediately. * * If a get or a put comes in with a relative path that tries to get outside of the serverDirectory, then the get or put will be denied. * * GET_ONLY mode only allows gets, PUT_ONLY mode only allows puts, and GET_AND_PUT allows both. Modes are defined as int constants in this class. * * @param serverReadDirectory directory for GET requests * @param serverWriteDirectory directory for PUT requests * @param port the port to use * @param localiface The local network interface to bind to. The interface's first address wil be used. * @param mode A value as specified above. * @param log Stream to write log message to. If not provided, uses System.out * @param errorLog Stream to write error messages to. If not provided, uses System.err. * @throws IOException if the server directory is invalid or does not exist. */ public TFTPServer(final File serverReadDirectory, final File serverWriteDirectory, final int port, final NetworkInterface localiface, final ServerMode mode, final PrintStream log, final PrintStream errorLog) throws IOException { mode_ = mode; port_ = port; InetAddress iaddr = null; if (localiface != null) { final Enumeration ifaddrs = localiface.getInetAddresses(); if ((ifaddrs != null) && ifaddrs.hasMoreElements()) { iaddr = ifaddrs.nextElement(); } } log_ = log == null ? nullStream : log; logError_ = errorLog == null ? nullStream : errorLog; laddr_ = iaddr; launch(serverReadDirectory, serverWriteDirectory); } /** * Start a TFTP Server on the specified port. Gets and Puts occur in the specified directory. * * The server will start in another thread, allowing this constructor to return immediately. * * If a get or a put comes in with a relative path that tries to get outside of the serverDirectory, then the get or put will be denied. * * GET_ONLY mode only allows gets, PUT_ONLY mode only allows puts, and GET_AND_PUT allows both. Modes are defined as int constants in this class. * * @param serverReadDirectory directory for GET requests * @param serverWriteDirectory directory for PUT requests * @param port the port to use * @param mode A value as specified above. * @param log Stream to write log message to. If not provided, uses System.out * @param errorLog Stream to write error messages to. If not provided, uses System.err. * @throws IOException if the server directory is invalid or does not exist. */ public TFTPServer(final File serverReadDirectory, final File serverWriteDirectory, final int port, final ServerMode mode, final PrintStream log, final PrintStream errorLog) throws IOException { port_ = port; mode_ = mode; log_ = log == null ? nullStream : log; logError_ = errorLog == null ? nullStream : errorLog; laddr_ = null; launch(serverReadDirectory, serverWriteDirectory); } /** * Start a TFTP Server on the default port (69). Gets and Puts occur in the specified directories. * * The server will start in another thread, allowing this constructor to return immediately. * * If a get or a put comes in with a relative path that tries to get outside of the serverDirectory, then the get or put will be denied. * * GET_ONLY mode only allows gets, PUT_ONLY mode only allows puts, and GET_AND_PUT allows both. Modes are defined as int constants in this class. * * @param serverReadDirectory directory for GET requests * @param serverWriteDirectory directory for PUT requests * @param mode A value as specified above. * @throws IOException if the server directory is invalid or does not exist. */ public TFTPServer(final File serverReadDirectory, final File serverWriteDirectory, final ServerMode mode) throws IOException { this(serverReadDirectory, serverWriteDirectory, DEFAULT_TFTP_PORT, mode, null, null); } @Override protected void finalize() throws Throwable { shutdown(); } /** * Get the current value for maxTimeoutRetries * * @return the max allowed number of retries */ public int getMaxTimeoutRetries() { return maxTimeoutRetries_; } /** * The current socket timeout used during transfers in milliseconds. * * @return the timeout value */ public int getSocketTimeout() { return socketTimeout_; } /** * check if the server thread is still running. * * @return true if running, false if stopped. * @throws Exception throws the exception that stopped the server if the server is stopped from an exception. */ public boolean isRunning() throws Exception { if (shutdownServer && serverException != null) { throw serverException; } return !shutdownServer; } /* * start the server, throw an error if it can't start. */ private void launch(final File serverReadDirectory, final File serverWriteDirectory) throws IOException { log_.println("Starting TFTP Server on port " + port_ + ". Read directory: " + serverReadDirectory + " Write directory: " + serverWriteDirectory + " Server Mode is " + mode_); serverReadDirectory_ = serverReadDirectory.getCanonicalFile(); if (!serverReadDirectory_.exists() || !serverReadDirectory.isDirectory()) { throw new IOException("The server read directory " + serverReadDirectory_ + " does not exist"); } serverWriteDirectory_ = serverWriteDirectory.getCanonicalFile(); if (!serverWriteDirectory_.exists() || !serverWriteDirectory.isDirectory()) { throw new IOException("The server write directory " + serverWriteDirectory_ + " does not exist"); } serverTftp_ = new TFTP(); // This is the value used in response to each client. socketTimeout_ = serverTftp_.getDefaultTimeout(); // we want the server thread to listen forever. serverTftp_.setDefaultTimeout(0); if (laddr_ != null) { serverTftp_.open(port_, laddr_); } else { serverTftp_.open(port_); } serverThread = new Thread(this); serverThread.setDaemon(true); serverThread.start(); } /* * Allow test code to customise the TFTP instance */ TFTP newTFTP() { return new TFTP(); } @Override public void run() { try { while (!shutdownServer) { final TFTPPacket tftpPacket; tftpPacket = serverTftp_.receive(); final TFTPTransfer tt = new TFTPTransfer(tftpPacket); synchronized (transfers_) { transfers_.add(tt); } final Thread thread = new Thread(tt); thread.setDaemon(true); thread.start(); } } catch (final Exception e) { if (!shutdownServer) { serverException = e; logError_.println("Unexpected Error in TFTP Server - Server shut down! + " + e); } } finally { shutdownServer = true; // set this to true, so the launching thread can check to see if it started. if (serverTftp_ != null && serverTftp_.isOpen()) { serverTftp_.close(); } } } /* * Also allow customisation of sending data/ack so can generate errors if needed */ void sendData(final TFTP tftp, final TFTPPacket data) throws IOException { tftp.bufferedSend(data); } /** * Set the stream object to log debug / informational messages. By default, this is a no-op * * @param log the stream to use for logging */ public void setLog(final PrintStream log) { this.log_ = log; } /** * Set the stream object to log error messsages. By default, this is a no-op * * @param logError the stream to use for logging errors */ public void setLogError(final PrintStream logError) { this.logError_ = logError; } /** * Set the max number of retries in response to a timeout. Default 3. Min 0. * * @param retries number of retries, must be > 0 */ public void setMaxTimeoutRetries(final int retries) { if (retries < 0) { throw new RuntimeException("Invalid Value"); } maxTimeoutRetries_ = retries; } /** * Set the socket timeout in milliseconds used in transfers. Defaults to the value here: * https://commons.apache.org/net/apidocs/org/apache/commons/net/tftp/TFTP.html#DEFAULT_TIMEOUT (5000 at the time I write this) Min value of 10. * * @param timeout the timeout; must be larger than 10 */ public void setSocketTimeout(final int timeout) { if (timeout < 10) { throw new RuntimeException("Invalid Value"); } socketTimeout_ = timeout; } /** * Stop the tftp server (and any currently running transfers) and release all opened network resources. */ public void shutdown() { shutdownServer = true; synchronized (transfers_) { transfers_.forEach(TFTPTransfer::shutdown); } try { serverTftp_.close(); } catch (final RuntimeException e) { // noop } try { serverThread.join(); } catch (final InterruptedException e) { // we've done the best we could, return } } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/tftp/TFTPServerMain.java000066400000000000000000000125271434047722200323440ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * Main class for TFTPServer. This allows CLI use of the server. * * @since 3.6 */ public class TFTPServerMain { private static final String USAGE = "Usage: TFTPServerMain [options] [port]\n\n" + "port - the port to use (default 6901)\n" + "\t-p path to server directory (default java.io.tempdir)\n" + "\t-r randomly introduce errors\n" + "\t-v verbose (trace packets)\n"; public static void main(final String[] args) throws Exception { int port = 6901; int argc; final Map opts = new HashMap<>(); opts.put("-p", System.getProperty("java.io.tmpdir")); // Parse options for (argc = 0; argc < args.length; argc++) { final String arg = args[argc]; if (!arg.startsWith("-")) { break; } if (arg.equals("-v") || arg.equals("-r")) { opts.put(arg, arg); } else if (arg.equals("-p")) { opts.put(arg, args[++argc]); } else { System.err.println("Error: unrecognized option."); System.err.print(USAGE); System.exit(1); } } if (argc < args.length) { port = Integer.parseInt(args[argc]); argc++; } final boolean verbose = opts.containsKey("-v"); final boolean randomErrors = opts.containsKey("-r"); final Random rand = randomErrors ? new Random() : null; final File serverDirectory = new File(opts.get("-p")); System.out.println("Server directory: " + serverDirectory); final TFTPServer tftpS = new TFTPServer(serverDirectory, serverDirectory, port, TFTPServer.ServerMode.GET_AND_PUT, null, null) { @Override TFTP newTFTP() { if (verbose) { return new TFTP() { @Override protected void trace(final String direction, final TFTPPacket packet) { System.out.println(direction + " " + packet.toString()); } }; } return new TFTP(); } @Override void sendData(final TFTP tftp, final TFTPPacket packet) throws IOException { if (rand == null) { super.sendData(tftp, packet); return; } final int rint = rand.nextInt(10); switch (rint) { case 0: System.out.println("Bump port " + packet); final int port = packet.getPort(); packet.setPort(port + 5); super.sendData(tftp, packet); packet.setPort(port); break; case 1: if (packet instanceof TFTPDataPacket) { final TFTPDataPacket data = (TFTPDataPacket) packet; System.out.println("Change data block num"); data.blockNumber--; super.sendData(tftp, packet); data.blockNumber++; } if (packet instanceof TFTPAckPacket) { final TFTPAckPacket ack = (TFTPAckPacket) packet; System.out.println("Change ack block num"); ack.blockNumber--; super.sendData(tftp, packet); ack.blockNumber++; } break; case 2: System.out.println("Drop packet: " + packet); break; case 3: System.out.println("Dupe packet: " + packet); super.sendData(tftp, packet); super.sendData(tftp, packet); break; default: super.sendData(tftp, packet); break; } } }; Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Server shutting down"); tftpS.shutdown(); System.out.println("Server exit"); } }); System.out.println("Started the server on " + port); Thread.sleep(99999999L); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/tftp/TFTPServerPathTest.java000066400000000000000000000121541434047722200332100ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import org.apache.commons.net.tftp.TFTPServer.ServerMode; import junit.framework.TestCase; /** * Some basic tests to ensure that the TFTP Server is honoring its read/write mode, and preventing files from being read or written from outside of the assigned * roots. */ public class TFTPServerPathTest extends TestCase { private static final int SERVER_PORT = 6901; String filePrefix = "tftp-"; File serverDirectory = new File(System.getProperty("java.io.tmpdir")); public void testReadOnly() throws IOException { // Start a read-only server final TFTPServer tftpS = new TFTPServer(serverDirectory, serverDirectory, SERVER_PORT, ServerMode.GET_ONLY, null, null); // Create our TFTP instance to handle the file transfer. final TFTPClient tftp = new TFTPClient(); tftp.open(); tftp.setSoTimeout(2000); // make a file to work with. final File file = new File(serverDirectory, filePrefix + "source.txt"); file.createNewFile(); // Read the file from the tftp server. final File out = new File(serverDirectory, filePrefix + "out"); // cleanup old failed runs out.delete(); assertFalse("Couldn't clear output location", out.exists()); try (final FileOutputStream output = new FileOutputStream(out)) { tftp.receiveFile(file.getName(), TFTP.BINARY_MODE, output, "localhost", SERVER_PORT); } assertTrue("file not created", out.exists()); out.delete(); try (final FileInputStream fis = new FileInputStream(file)) { tftp.sendFile(out.getName(), TFTP.BINARY_MODE, fis, "localhost", SERVER_PORT); fail("Server allowed write"); } catch (final IOException e) { // expected path } file.delete(); tftpS.shutdown(); } public void testWriteOnly() throws IOException { // Start a write-only server final TFTPServer tftpS = new TFTPServer(serverDirectory, serverDirectory, SERVER_PORT, ServerMode.PUT_ONLY, null, null); // Create our TFTP instance to handle the file transfer. final TFTPClient tftp = new TFTPClient(); tftp.open(); tftp.setSoTimeout(2000); // make a file to work with. final File file = new File(serverDirectory, filePrefix + "source.txt"); file.createNewFile(); final File out = new File(serverDirectory, filePrefix + "out"); // cleanup old failed runs out.delete(); assertFalse("Couldn't clear output location", out.exists()); try (final FileOutputStream output = new FileOutputStream(out)) { tftp.receiveFile(file.getName(), TFTP.BINARY_MODE, output, "localhost", SERVER_PORT); fail("Server allowed read"); } catch (final IOException e) { // expected path } out.delete(); try (final FileInputStream fis = new FileInputStream(file)) { tftp.sendFile(out.getName(), TFTP.BINARY_MODE, fis, "localhost", SERVER_PORT); } assertTrue("file not created", out.exists()); // cleanup file.delete(); out.delete(); tftpS.shutdown(); } public void testWriteOutsideHome() throws IOException { // Start a server final TFTPServer tftpS = new TFTPServer(serverDirectory, serverDirectory, SERVER_PORT, ServerMode.GET_AND_PUT, null, null); // Create our TFTP instance to handle the file transfer. final TFTPClient tftp = new TFTPClient(); tftp.open(); final File file = new File(serverDirectory, filePrefix + "source.txt"); file.createNewFile(); assertFalse("test construction error", new File(serverDirectory, "../foo").exists()); try (final FileInputStream fis = new FileInputStream(file)) { tftp.sendFile("../foo", TFTP.BINARY_MODE, fis, "localhost", SERVER_PORT); fail("Server allowed write!"); } catch (final IOException e) { // expected path } assertFalse("file created when it should not have been", new File(serverDirectory, "../foo").exists()); // cleanup file.delete(); tftpS.shutdown(); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/tftp/TFTPTest.java000066400000000000000000000164241434047722200312100ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.tftp; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; 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 org.apache.commons.net.tftp.TFTPServer.ServerMode; import junit.framework.TestCase; /** * Test the TFTP Server and TFTP Client by creating some files in the system temp folder and then uploading and downloading them. */ public class TFTPTest extends TestCase { private static final int SERVER_PORT = 6902; private static TFTPServer tftpS; private static final File serverDirectory = new File(System.getProperty("java.io.tmpdir")); private static final String filePrefix = "tftp-"; private static final File[] files = new File[8]; static int testsLeftToRun = 6; // only want to do this once... static { try { files[0] = createFile(new File(serverDirectory, filePrefix + "empty.txt"), 0); files[1] = createFile(new File(serverDirectory, filePrefix + "small.txt"), 1); files[2] = createFile(new File(serverDirectory, filePrefix + "511.txt"), 511); files[3] = createFile(new File(serverDirectory, filePrefix + "512.txt"), 512); files[4] = createFile(new File(serverDirectory, filePrefix + "513.txt"), 513); files[5] = createFile(new File(serverDirectory, filePrefix + "med.txt"), 1000 * 1024); files[6] = createFile(new File(serverDirectory, filePrefix + "big.txt"), 5000 * 1024); files[7] = createFile(new File(serverDirectory, filePrefix + "huge.txt"), 37000 * 1024); // Start the server tftpS = new TFTPServer(serverDirectory, serverDirectory, SERVER_PORT, ServerMode.GET_AND_PUT, null, null); tftpS.setSocketTimeout(2000); } catch (final IOException e) { e.printStackTrace(); } } /* * Create a file, size specified in bytes */ private static File createFile(final File file, final int size) throws IOException { try (final OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) { final byte[] temp = "0".getBytes(); for (int i = 0; i < size; i++) { os.write(temp); } } return file; } private boolean filesIdentical(final File a, final File b) throws IOException { if (!a.exists() || !b.exists()) { return false; } if (a.length() != b.length()) { return false; } try (final InputStream fisA = new BufferedInputStream(new FileInputStream(a)); final InputStream fisB = new BufferedInputStream(new FileInputStream(b))) { int aBit = fisA.read(); int bBit = fisB.read(); while (aBit != -1) { if (aBit != bBit) { fisA.close(); fisB.close(); return false; } aBit = fisA.read(); bBit = fisB.read(); } } return true; } @Override protected void tearDown() throws Exception { testsLeftToRun--; if (testsLeftToRun <= 0) { if (tftpS != null) { tftpS.shutdown(); } for (final File file : files) { file.delete(); } } super.tearDown(); } public void testASCIIDownloads() { // test with the smaller files for (int i = 0; i < 6; i++) { try { testDownload(TFTP.ASCII_MODE, files[i]); } catch (final IOException e) { fail("Entry " + i + " Error " + e.toString()); } } } public void testASCIIUploads() throws Exception { // test with the smaller files for (int i = 0; i < 6; i++) { testUpload(TFTP.ASCII_MODE, files[i]); } } private void testDownload(final int mode, final File file) throws IOException { // Create our TFTP instance to handle the file transfer. final TFTPClient tftp = new TFTPClient(); tftp.open(); tftp.setSoTimeout(2000); final File out = new File(serverDirectory, filePrefix + "download"); // cleanup old failed runs out.delete(); assertFalse("Couldn't clear output location", out.exists()); try (final FileOutputStream output = new FileOutputStream(out)) { tftp.receiveFile(file.getName(), mode, output, "localhost", SERVER_PORT); } assertTrue("file not created", out.exists()); assertTrue("files not identical on file " + file, filesIdentical(out, file)); // delete the downloaded file out.delete(); } public void testHugeDownloads() throws Exception { // test with the smaller files for (int i = 5; i < files.length; i++) { testDownload(TFTP.BINARY_MODE, files[i]); } } public void testHugeUploads() throws Exception { for (int i = 5; i < files.length; i++) { testUpload(TFTP.BINARY_MODE, files[i]); } } public void testTFTPBinaryDownloads() throws Exception { // test with the smaller files for (int i = 0; i < 6; i++) { testDownload(TFTP.BINARY_MODE, files[i]); } } public void testTFTPBinaryUploads() throws Exception { // test with the smaller files for (int i = 0; i < 6; i++) { testUpload(TFTP.BINARY_MODE, files[i]); } } private void testUpload(final int mode, final File file) throws Exception { // Create our TFTP instance to handle the file transfer. final TFTPClient tftp = new TFTPClient(); tftp.open(); tftp.setSoTimeout(2000); final File in = new File(serverDirectory, filePrefix + "upload"); // cleanup old failed runs in.delete(); assertFalse("Couldn't clear output location", in.exists()); try (final FileInputStream fis = new FileInputStream(file)) { tftp.sendFile(in.getName(), mode, fis, "localhost", SERVER_PORT); } // need to give the server a bit of time to receive our last packet, and // close out its file buffers, etc. Thread.sleep(100); assertTrue("file not created", in.exists()); assertTrue("files not identical on file " + file, filesIdentical(file, in)); in.delete(); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/time/000077500000000000000000000000001434047722200267025ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/time/TimeTCPClientTest.java000066400000000000000000000100661434047722200330140ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.time; import java.io.IOException; import java.net.InetAddress; import java.util.Calendar; import java.util.TimeZone; import junit.framework.TestCase; public class TimeTCPClientTest extends TestCase { private TimeTestSimpleServer server1; private int _port = 3333; // default test port protected void closeConnections() { try { server1.stop(); Thread.sleep(1000); } catch (final Exception e) { // ignored } } protected void openConnections() throws Exception { try { server1 = new TimeTestSimpleServer(_port); server1.connect(); } catch (final IOException ioe) { // try again on another port _port = 4000; server1 = new TimeTestSimpleServer(_port); server1.connect(); } server1.start(); } /* * tests the times retrieved via the Time protocol implementation. */ public void testCompareTimes() throws Exception { openConnections(); long time, time2; long clientTime, clientTime2; final TimeTCPClient client = new TimeTCPClient(); try { // Not sure why code used to use getLocalHost. final InetAddress localHost = InetAddress.getByName("localhost"); // WAS InetAddress.getLocalHost(); try { // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.connect(localHost, _port); clientTime = client.getDate().getTime(); time = System.currentTimeMillis(); } catch (final IOException e) { // catch the first connect error; assume second will work if this does fail("IOError <" + e + "> trying to connect to " + localHost + " " + _port); throw e; } finally { if (client.isConnected()) { client.disconnect(); } } try { // We want to timeout if a response takes longer than 60 seconds client.setDefaultTimeout(60000); client.connect(localHost, _port); clientTime2 = (client.getTime() - TimeTCPClient.SECONDS_1900_TO_1970) * 1000L; time2 = System.currentTimeMillis(); } finally { if (client.isConnected()) { client.disconnect(); } } } finally { closeConnections(); } // current time shouldn't differ from time reported via network by 5 seconds assertTrue(Math.abs(time - clientTime) < 5000); assertTrue(Math.abs(time2 - clientTime2) < 5000); } /* * tests the constant basetime used by TimeClient against tha computed from Calendar class. */ public void testInitial() { final TimeZone utcZone = TimeZone.getTimeZone("UTC"); final Calendar calendar = Calendar.getInstance(utcZone); calendar.set(1900, Calendar.JANUARY, 1, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); final long baseTime = calendar.getTime().getTime() / 1000L; assertEquals(baseTime, -TimeTCPClient.SECONDS_1900_TO_1970); } } TimeTestSimpleServer.java000066400000000000000000000100541434047722200335650ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/time/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.net.time; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * The TimetSimpleServer class is a simple TCP implementation of a server for the Time Protocol described in RFC 868. *

* Listens for TCP socket connections on the time protocol port and writes the local time to socket outputStream as 32-bit integer of seconds since midnight on * 1 January 1900 GMT. See the spec for details. *

* Note this is for debugging purposes only and not meant to be run as a realiable time service. * */ public class TimeTestSimpleServer implements Runnable { /** * baseline time 1900-01-01T00:00:00 UTC */ public static final long SECONDS_1900_TO_1970 = 2208988800L; /** The default time port. It is set to 37 according to RFC 868. */ public static final int DEFAULT_PORT = 37; public static void main(final String[] args) { final TimeTestSimpleServer server = new TimeTestSimpleServer(); try { server.start(); } catch (final IOException e) { // ignored } } private ServerSocket server; private final int port; private boolean running; public TimeTestSimpleServer() { port = DEFAULT_PORT; } public TimeTestSimpleServer(final int port) { this.port = port; } public void connect() throws IOException { if (server == null) { server = new ServerSocket(port); } } public int getPort() { return server == null ? port : server.getLocalPort(); } public boolean isRunning() { return running; } @Override public void run() { Socket socket = null; while (running) { try { socket = server.accept(); final DataOutputStream os = new DataOutputStream(socket.getOutputStream()); // add 500 ms to round off to nearest second final int time = (int) ((System.currentTimeMillis() + 500) / 1000 + SECONDS_1900_TO_1970); os.writeInt(time); os.flush(); } catch (final IOException e) { // ignored } finally { if (socket != null) { try { socket.close(); // force closing of the socket } catch (final IOException e) { System.err.println("close socket error: " + e); } } } } } /* * Start time service and provide time to client connections. */ public void start() throws IOException { if (server == null) { connect(); } if (!running) { running = true; new Thread(this).start(); } } /* * Close server socket. */ public void stop() { running = false; if (server != null) { try { server.close(); // force closing of the socket } catch (final IOException e) { System.err.println("close socket error: " + e); } server = null; } } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/util/000077500000000000000000000000001434047722200267215ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/util/Base64Test.java000066400000000000000000000137301434047722200314540ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.util; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Ignore; import org.junit.Test; public class Base64Test { @Test public void testBase64() { final Base64 b64 = new Base64(); assertFalse(b64.isUrlSafe()); } @Test public void testBase64Boolean() { final Base64 b64 = new Base64(true); assertTrue(b64.isUrlSafe()); assertArrayEquals(new byte[] { '\r', '\n' }, b64.getLineSeparator()); } @Test public void testBase64Int() { Base64 b64; b64 = new Base64(8); assertFalse(b64.isUrlSafe()); assertEquals(8, b64.getLineLength()); b64 = new Base64(11); assertEquals(8, b64.getLineLength()); } @Test public void testBase64IntByteArray() { final Base64 b64; b64 = new Base64(8, new byte[] {}); assertFalse(b64.isUrlSafe()); assertArrayEquals(new byte[] {}, b64.getLineSeparator()); } @Test public void testBase64IntByteArrayBoolean() { Base64 b64; b64 = new Base64(8, new byte[] {}, false); assertFalse(b64.isUrlSafe()); b64 = new Base64(8, new byte[] {}, true); assertTrue(b64.isUrlSafe()); } @Test @Ignore public void testDecodeBase64ByteArray() { fail("Not yet implemented"); } @Test @Ignore public void testDecodeBase64String() { fail("Not yet implemented"); } @Test @Ignore public void testDecodeByteArray() { fail("Not yet implemented"); } @Test @Ignore public void testDecodeInteger() { fail("Not yet implemented"); } @Test @Ignore public void testDecodeObject() { fail("Not yet implemented"); } @Test @Ignore public void testDecodeString() { fail("Not yet implemented"); } @Test public void testEncodeBase64ByteArray() { final byte[] binaryData = null; assertArrayEquals(binaryData, Base64.encodeBase64(binaryData)); } @Test @Ignore public void testEncodeBase64ByteArrayBoolean() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeBase64ByteArrayBooleanBoolean() { fail("Not yet implemented"); } @Test public void testEncodeBase64ByteArrayBooleanBooleanInt() { final byte[] binaryData = { '1', '2', '3' }; byte[] encoded; encoded = Base64.encodeBase64(binaryData, false, false); assertNotNull(encoded); assertEquals(4, encoded.length); try { Base64.encodeBase64(binaryData, false, false, 3); fail("Expected IllegalArgumentException"); } catch (final IllegalArgumentException expected) { // expected } encoded = Base64.encodeBase64(binaryData, false, false, 4); // NET-483 assertNotNull(encoded); assertEquals(4, encoded.length); encoded = Base64.encodeBase64(binaryData, true, false); assertNotNull(encoded); assertEquals(6, encoded.length); // always adds trailer try { Base64.encodeBase64(binaryData, true, false, 5); fail("Expected IllegalArgumentException"); } catch (final IllegalArgumentException expected) { // expected } encoded = Base64.encodeBase64(binaryData, true, false, 6); assertNotNull(encoded); assertEquals(6, encoded.length); } @Test @Ignore public void testEncodeBase64Chunked() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeBase64StringByteArray() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeBase64StringByteArrayBoolean() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeBase64StringUnChunked() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeBase64URLSafe() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeBase64URLSafeString() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeByteArray() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeInteger() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeObject() { fail("Not yet implemented"); } @Test @Ignore public void testEncodeToString() { fail("Not yet implemented"); } @Test public void testIsArrayByteBase64() { assertTrue(Base64.isArrayByteBase64(new byte[] { 'b', ' ' })); assertFalse(Base64.isArrayByteBase64(new byte[] { '?' })); } @Test public void testIsBase64() { assertTrue(Base64.isBase64((byte) 'b')); assertFalse(Base64.isBase64((byte) ' ')); } @Test @Ignore public void testToIntegerBytes() { fail("Not yet implemented"); } } commons-net-rel-commons-net-3.9.0/src/test/java/org/apache/commons/net/util/UtilTest.java000066400000000000000000000207331434047722200313460ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.net.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.CharArrayReader; import java.io.CharArrayWriter; import java.io.Closeable; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.net.Socket; import org.apache.commons.net.io.CopyStreamEvent; import org.apache.commons.net.io.CopyStreamListener; import org.apache.commons.net.io.Util; import org.junit.Assert; import org.junit.Test; public class UtilTest { static class CSL implements CopyStreamListener { final long expectedTotal; final int expectedBytes; final long expectedSize; CSL(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { this.expectedTotal = totalBytesTransferred; this.expectedBytes = bytesTransferred; this.expectedSize = streamSize; } @Override public void bytesTransferred(final CopyStreamEvent event) { } @Override public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { Assert.assertEquals("Wrong total", expectedTotal, totalBytesTransferred); Assert.assertEquals("Wrong streamSize", expectedSize, streamSize); Assert.assertEquals("Wrong bytes", expectedBytes, bytesTransferred); } } // Class to check overall counts as well as batch size static class CSLtotal implements CopyStreamListener { final long expectedTotal; final long expectedBytes; volatile long totalBytesTransferredTotal; volatile long bytesTransferredTotal; CSLtotal(final long totalBytesTransferred, final long bytesTransferred) { this.expectedTotal = totalBytesTransferred; this.expectedBytes = bytesTransferred; } @Override public void bytesTransferred(final CopyStreamEvent event) { } @Override public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { Assert.assertEquals("Wrong bytes", expectedBytes, bytesTransferred); this.totalBytesTransferredTotal = totalBytesTransferred; this.bytesTransferredTotal += bytesTransferred; } void checkExpected() { Assert.assertEquals("Wrong totalBytesTransferred total", expectedTotal, totalBytesTransferredTotal); Assert.assertEquals("Total should equal sum of parts", totalBytesTransferredTotal, bytesTransferredTotal); } } private final Writer dest = new CharArrayWriter(); private final Reader source = new CharArrayReader(new char[] { 'a' }); private final InputStream src = new ByteArrayInputStream(new byte[] { 'z' }); private final OutputStream dst = new ByteArrayOutputStream(); @Test public void testcloseQuietly() { Util.closeQuietly((Closeable) null); Util.closeQuietly((Socket) null); } @Test public void testNET550_Reader() throws Exception { final char[] buff = { 'a', 'b', 'c', 'd' }; // must be multiple of 2 final int bufflen = buff.length; { // Check buffer size 1 processes in chunks of 1 final Reader rdr = new CharArrayReader(buff); final CSLtotal listener = new CSLtotal(bufflen, 1); Util.copyReader(rdr, dest, 1, 0, listener); // buffer size 1 listener.checkExpected(); } { // Check bufsize 2 uses chunks of 2 final Reader rdr = new CharArrayReader(buff); final CSLtotal listener = new CSLtotal(bufflen, 2); Util.copyReader(rdr, dest, 2, 0, listener); // buffer size 2 listener.checkExpected(); } { // Check bigger size reads the lot final Reader rdr = new CharArrayReader(buff); final CSLtotal listener = new CSLtotal(bufflen, bufflen); Util.copyReader(rdr, dest, 20, 0, listener); // buffer size 20 listener.checkExpected(); } { // Check negative size reads reads full amount final Reader rdr = new CharArrayReader(buff); final CSLtotal listener = new CSLtotal(bufflen, bufflen); Util.copyReader(rdr, dest, -1, 0, listener); // buffer size -1 listener.checkExpected(); } { // Check zero size reads reads full amount final Reader rdr = new CharArrayReader(buff); final CSLtotal listener = new CSLtotal(bufflen, bufflen); Util.copyReader(rdr, dest, 0, 0, listener); // buffer size -1 listener.checkExpected(); } } @Test public void testNET550_Stream() throws Exception { final byte[] buff = { 'a', 'b', 'c', 'd' }; // must be multiple of 2 final int bufflen = buff.length; { // Check buffer size 1 processes in chunks of 1 final InputStream is = new ByteArrayInputStream(buff); final CSLtotal listener = new CSLtotal(bufflen, 1); Util.copyStream(is, dst, 1, 0, listener); // buffer size 1 listener.checkExpected(); } { // Check bufsize 2 uses chunks of 2 final InputStream is = new ByteArrayInputStream(buff); final CSLtotal listener = new CSLtotal(bufflen, 2); Util.copyStream(is, dst, 2, 0, listener); // buffer size 2 listener.checkExpected(); } { // Check bigger size reads the lot final InputStream is = new ByteArrayInputStream(buff); final CSLtotal listener = new CSLtotal(bufflen, bufflen); Util.copyStream(is, dst, 20, 0, listener); // buffer size 20 listener.checkExpected(); } { // Check negative size reads reads full amount final InputStream is = new ByteArrayInputStream(buff); final CSLtotal listener = new CSLtotal(bufflen, bufflen); Util.copyStream(is, dst, -1, 0, listener); // buffer size -1 listener.checkExpected(); } { // Check zero size reads reads full amount final InputStream is = new ByteArrayInputStream(buff); final CSLtotal listener = new CSLtotal(bufflen, bufflen); Util.copyStream(is, dst, 0, 0, listener); // buffer size -1 listener.checkExpected(); } } @Test public void testReader_1() throws Exception { final long streamSize = 0; final int bufferSize = -1; Util.copyReader(source, dest, bufferSize, streamSize, new CSL(1, 1, streamSize)); } @Test public void testReader0() throws Exception { final long streamSize = 0; final int bufferSize = 0; Util.copyReader(source, dest, bufferSize, streamSize, new CSL(1, 1, streamSize)); } @Test public void testReader1() throws Exception { final long streamSize = 0; final int bufferSize = 1; Util.copyReader(source, dest, bufferSize, streamSize, new CSL(1, 1, streamSize)); } @Test public void testStream_1() throws Exception { final long streamSize = 0; final int bufferSize = -1; Util.copyStream(src, dst, bufferSize, streamSize, new CSL(1, 1, streamSize)); } @Test public void testStream0() throws Exception { final long streamSize = 0; final int bufferSize = 0; Util.copyStream(src, dst, bufferSize, streamSize, new CSL(1, 1, streamSize)); } @Test public void testStream1() throws Exception { final long streamSize = 0; final int bufferSize = 1; Util.copyStream(src, dst, bufferSize, streamSize, new CSL(1, 1, streamSize)); } } commons-net-rel-commons-net-3.9.0/src/test/resources/000077500000000000000000000000001434047722200225645ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/000077500000000000000000000000001434047722200233535ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/000077500000000000000000000000001434047722200245745ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/commons/000077500000000000000000000000001434047722200262475ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/commons/net/000077500000000000000000000000001434047722200270355ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/commons/net/ftpsserver/000077500000000000000000000000001434047722200312405ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/commons/net/ftpsserver/ftpserver.jks000066400000000000000000000057531434047722200340030ustar00rootroot00000000000000 ftpservert00 +*X »9XAf}95'z]3gnKم w1\%p|EEv1m|*+!"Zrl+a#߆EEsP+B>Hmu ;xv z-*B.J8,i)JmFNC"/3Ï{jt*Gɸ.J?PNa53\+$[~;xՌ^a2ɡ_:Ş q P,DNV# .Aջ< 9&I]i&F<@$*EJpD>㧼< =T1?/~fIX?nX嫣-Z+hHi*5ɖIkF9Ť)rC zZM>mg3.[UCRlKx@H-rᓼ144=x8+nj:3نV ɇ/̫ɪa&,5-}j#d?ocj3)L~OBŰjuFH/maECJb;._0w%-?q1)[E \tBXu\@E\-Do ){(HmrĞ&ZZ~-aenȡKw̓ ?SqDA&3 3w8x> T.tf5=T4.APȏ9=}b) Wu3vCd$R< og4e~2U6%e$\6 *^JFKFa,gZ>s37I@R5R# i7Q = ߖH$s &<5 (m)(h.1x} 8&Bϵi &[Lݍ֞anPnZ|L1G`/~V(,H/f$;5 Ӝ#X.509s0o0W/n0  *H  0h1 0 UUS10UUnknown10UUnknown10 U Apache10U Unknown10U localhost0 201003061414Z 300812061414Z0h1 0 UUS10UUnknown10UUnknown10 U Apache10U Unknown10U localhost0"0  *H 0 ֞*wMot(Q4|Φ, +gh18B4vm9ON}}V9<AxNLcb"n`Hz\Xڞv8d"SK|_#w%oD,e Cq7zI;[CӅ[Cɔ'E,{DiӔiP<@;ƒ@I9b*RVyN>@b藍xgCo [:0kL|'yB[BAШCY ͗6!,u!00UkF k)0  *H  0E((o*o\h(2@\/rT;)ؑxX'b2)߈bPGMTaV‚afjKgsl[5(8R+YxV]+ G\pW )LH\olu&[b?)ag !gViz#=wFvGSYʅLs!Z^ aÄ5z3m*wX ftpclient9OX.509 00EEd0 *H80i1 0 USE10UUnknown10UUnknown10U Unknown10U Unknown10U FtpClient0 061029203900Z 260716203900Z0i1 0 USE10UUnknown10UUnknown10U Unknown10U Unknown10U FtpClient00,*H80Su)RJ.R`$ 9ܒT-H{#:\-9iBNDUxH0k䠬b#+[9fme0 *H800-\%X{9"- aVh\ ?nh'IH'ejI)\*jC5xusers.properties000066400000000000000000000035501434047722200344430ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/commons/net/ftpsserver# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # Password is "admin" ftpserver.user.admin.userpassword=21232F297A57A5A743894A0E4A801FC3 ftpserver.user.admin.homedirectory=target/test-classes/org/apache/commons/net/test-data ftpserver.user.admin.enableflag=true ftpserver.user.admin.writepermission=true ftpserver.user.admin.maxloginnumber=0 ftpserver.user.admin.maxloginperip=0 ftpserver.user.admin.idletime=0 ftpserver.user.admin.uploadrate=0 ftpserver.user.admin.downloadrate=0 ftpserver.user.anonymous.userpassword= ftpserver.user.anonymous.homedirectory=target/test-classes/org/apache/commons/net/test-data ftpserver.user.anonymous.enableflag=true ftpserver.user.anonymous.writepermission=false ftpserver.user.anonymous.maxloginnumber=20 ftpserver.user.anonymous.maxloginperip=2 ftpserver.user.anonymous.idletime=300 ftpserver.user.anonymous.uploadrate=4800 ftpserver.user.anonymous.downloadrate=4800 # password is "test" ftpserver.user.test.userpassword=098f6bcd4621d373cade4e832627b4f6 ftpserver.user.test.homedirectory=target/test-classes/org/apache/commons/net/test-data ftpserver.user.test.enableflag=true ftpserver.user.test.writepermission=true commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/commons/net/test-data/000077500000000000000000000000001434047722200307235ustar00rootroot00000000000000commons-net-rel-commons-net-3.9.0/src/test/resources/org/apache/commons/net/test-data/file.txt000066400000000000000000000016021434047722200324020ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. “We are all in the gutter, but some of us are looking at the stars.” ― Oscar Wilde, Lady Windermere's Fan