68 Commits

Author SHA1 Message Date
dependabot[bot]
650c117214 Bump coverallsapp/github-action from 2.3.0 to 2.3.4
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.3.0 to 2.3.4.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](643bc377ff...cfd0633edb)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-25 15:18:43 +00:00
dependabot[bot]
1b970516f5 Bump actions/checkout from 4.1.6 to 4.1.7 (#385)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.6 to 4.1.7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.6...v4.1.7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 19:33:26 -04:00
dependabot[bot]
d0a6cb31a2 Bump golangci/golangci-lint-action from 6.0.1 to 6.1.0 (#389)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 6.0.1 to 6.1.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](a4f60bb28d...aaa42aa062)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 19:33:13 -04:00
dependabot[bot]
1c1a6e4887 Bump actions/setup-go from 5.0.1 to 5.0.2 (#388)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](cdcb360436...0a12ed9d6a)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 19:33:00 -04:00
dependabot[bot]
ebfb9b5098 Bump actions/setup-go from 5.0.0 to 5.0.1 (#375)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](0c52d547c9...cdcb360436)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 17:45:10 -04:00
dependabot[bot]
7844f623b3 Bump coverallsapp/github-action from 2.2.3 to 2.3.0 (#380)
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.2.3 to 2.3.0.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](3dfc556739...643bc377ff)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 17:45:01 -04:00
dependabot[bot]
2304047ac3 Bump golangci/golangci-lint-action from 4.0.0 to 6.0.1 (#379)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4.0.0 to 6.0.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](3cfe3a4abb...a4f60bb28d)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 17:44:46 -04:00
dependabot[bot]
15617cd936 Bump github.com/stretchr/testify from 1.8.4 to 1.9.0 (#365)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.4 to 1.9.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.4...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 17:44:28 -04:00
dependabot[bot]
585184769d Bump actions/cache from 4.0.0 to 4.0.2 (#368)
Bumps [actions/cache](https://github.com/actions/cache) from 4.0.0 to 4.0.2.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](13aacd865c...0c45773b62)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 17:44:10 -04:00
dependabot[bot]
557e170e29 Bump actions/checkout from 4.1.1 to 4.1.6 (#381)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.1...v4.1.6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 17:43:55 -04:00
dependabot[bot]
4edb16bfcd Bump golangci/golangci-lint-action from 3.7.0 to 4.0.0 (#364)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.7.0 to 4.0.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](3a91952989...3cfe3a4abb)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-14 17:45:49 -05:00
dependabot[bot]
649c613fbc Bump actions/cache from 3.3.2 to 4.0.0 (#360)
Bumps [actions/cache](https://github.com/actions/cache) from 3.3.2 to 4.0.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](704facf57e...13aacd865c)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-14 17:45:40 -05:00
Jean-Luc Lacroix
a93533d16c New date formats for non standard dates returned by LIST (#354) 2024-01-11 14:31:46 -05:00
dependabot[bot]
824fdbc882 Bump coverallsapp/github-action from 2.2.1 to 2.2.3 (#351)
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.2.1 to 2.2.3.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](95b1a2355b...3dfc556739)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 14:30:55 -05:00
dependabot[bot]
678cef9c13 Bump actions/cache from 3.3.1 to 3.3.2 (#352)
Bumps [actions/cache](https://github.com/actions/cache) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](88522ab9f3...704facf57e)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 14:30:44 -05:00
dependabot[bot]
d019ddee92 Bump actions/checkout from 3.6.0 to 4.1.1 (#353)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.1.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.6.0...v4.1.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 14:30:31 -05:00
dependabot[bot]
a9581d60ae Bump actions/setup-go from 4.1.0 to 5.0.0 (#355)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4.1.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](93397bea11...0c52d547c9)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 14:30:17 -05:00
dependabot[bot]
f58b250a0a Bump github/codeql-action from 2 to 3 (#357)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 14:30:06 -05:00
dependabot[bot]
bedccfb795 Bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#331)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.3 to 1.8.4.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 14:57:19 -05:00
Tamás Gulácsi
e44fc64e60 Fix PASV address like lftp does (#349)
Some behind-firewall-and-corporate-network FTP servers
responds their private-net address instead of the
publicly reachable.

This fix checks for such address and uses the
command channel's address instead.
2023-11-13 14:56:59 -05:00
dependabot[bot]
a58dc069db Bump golangci/golangci-lint-action from 3.4.0 to 3.7.0 (#344)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.4.0 to 3.7.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](08e2f20817...3a91952989)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 13:46:08 -05:00
dependabot[bot]
22efdfbe8a Bump actions/setup-go from 4.0.1 to 4.1.0 (#343)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4.0.1 to 4.1.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](fac708d667...93397bea11)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 13:45:53 -05:00
dependabot[bot]
9226fc7f7c Bump coverallsapp/github-action from 2.1.2 to 2.2.1 (#341)
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.1.2 to 2.2.1.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](f350da2c03...95b1a2355b)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 13:45:36 -05:00
dependabot[bot]
2b809c0747 Bump actions/checkout from 2.4.2 to 3.6.0 (#345)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.2 to 3.6.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.4.2...v3.6.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 13:45:21 -05:00
dependabot[bot]
2c2aa379fd Bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#330)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.3.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.3)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 17:29:40 -04:00
dependabot[bot]
f5852338bb Bump actions/setup-go from 4.0.0 to 4.0.1 (#328)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](4d34df0c23...fac708d667)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 17:29:21 -04:00
Nick Craig-Wood
36e873b513 Fix GetEntry / MLST for some servers - Fixes #321 (#322)
Some servers seem to send a blank line at the end of an MLST response.

    MLST Download
    250-
    Size=53248;Modify=20230327134339.000;Type=dir; Download

    250 Requested file action okay, completed.

Before this change this would cause the GetEntry method to return this
error.

    unsupported LIST line

This patch ignores blank lines in the MLST response.
2023-05-12 22:29:37 -04:00
dependabot[bot]
0feadd74ba Bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#311)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-12 22:27:57 -04:00
dependabot[bot]
67ffbbc0de Bump actions/cache from 3.2.5 to 3.3.1 (#318)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.5 to 3.3.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](6998d139dd...88522ab9f3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-12 22:27:45 -04:00
dependabot[bot]
3eddb768d0 Bump actions/setup-go from 3.5.0 to 4.0.0 (#319)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3.5.0 to 4.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](6edd4406fa...4d34df0c23)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-12 22:27:33 -04:00
dependabot[bot]
f568a0d846 Bump coverallsapp/github-action from 1.1.3 to 2.1.2 (#327)
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 1.1.3 to 2.1.2.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](9ba913c152...f350da2c03)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-12 22:27:20 -04:00
dependabot[bot]
d84bf4be2b Bump actions/cache from 3.2.4 to 3.2.5 (#308)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.4 to 3.2.5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](627f0f41f6...6998d139dd)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 19:46:52 -05:00
Nick Craig-Wood
9e39e2c406 Fix hang when using ExplicitTLS to certain servers. (#283)
In #282 it was discovered that doing the tls Handshake immediately on
connection causes some FTP servers (proftpd and pureftpd) to hang.

The exact cause of this is unknown, but this patch works around the
problem by not doing the Handsake initially, and only doing it at the
end if we were attempting to upload a zero length file.

Doing the Handshake at the end was originally added in
a4e9650823 however it got reverted in 212daf295f which
used tls.DialWithDialer to do the handshake. Unfortunately
tls.DialWithDialer seems to trigger the hanging bug.

See: https://forum.rclone.org/t/rclone-ftps-explicit-rclone-touch-empty-files-proftpd-unable-to-build-data-connection-operation-not-permitted/22522
See: https://github.com/rclone/rclone/issues/6426#issuecomment-1243993039
Fixes #282
2023-02-08 11:46:41 -05:00
dependabot[bot]
58cb524052 Bump actions/cache from 3.2.3 to 3.2.4 (#307)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](58c146cc91...627f0f41f6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 11:44:47 -05:00
dependabot[bot]
0257a7bc42 Bump golangci/golangci-lint-action from 3.3.1 to 3.4.0 (#306)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](0ad9a0988b...08e2f20817)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-23 20:58:31 -05:00
dependabot[bot]
5fc8c694ec Bump actions/cache from 3.2.0 to 3.2.3 (#304)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.0 to 3.2.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](c17f4bf466...58c146cc91)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-16 13:19:23 -05:00
dependabot[bot]
b10ce93530 Bump github.com/stretchr/testify from 1.8.0 to 1.8.1 (#294)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-21 17:33:01 -05:00
dependabot[bot]
5b1ec4dc6c Bump golangci/golangci-lint-action from 3.2.0 to 3.3.1 (#297)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.2.0 to 3.3.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](537aa1903e...0ad9a0988b)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-21 17:32:28 -05:00
dependabot[bot]
a0ec57882b Bump actions/setup-go from 3.3.0 to 3.5.0 (#300)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3.3.0 to 3.5.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](268d8c0ca0...6edd4406fa)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-21 17:32:07 -05:00
dependabot[bot]
917837ffbd Bump actions/cache from 3.0.8 to 3.2.0 (#301)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.8 to 3.2.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](fd5de65bc8...c17f4bf466)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-21 17:31:46 -05:00
dependabot[bot]
6512c2a4ae Bump actions/setup-go from 2.2.0 to 3.3.0 (#280)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2.2.0 to 3.3.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](bfdd3570ce...268d8c0ca0)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 18:52:58 -04:00
dependabot[bot]
a2b3f0878b Bump coverallsapp/github-action from 1.1.2 to 1.1.3 (#274)
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v1.1.2...1.1.3)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 18:46:14 -04:00
dependabot[bot]
b40593bd5a Bump jandelgado/gcov2lcov-action from 1.0.8 to 1.0.9 (#273)
Bumps [jandelgado/gcov2lcov-action](https://github.com/jandelgado/gcov2lcov-action) from 1.0.8 to 1.0.9.
- [Release notes](https://github.com/jandelgado/gcov2lcov-action/releases)
- [Changelog](https://github.com/jandelgado/gcov2lcov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jandelgado/gcov2lcov-action/compare/v1.0.8...v1.0.9)

---
updated-dependencies:
- dependency-name: jandelgado/gcov2lcov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 18:45:43 -04:00
dependabot[bot]
dca029e125 Bump golangci/golangci-lint-action from 2.5.2 to 3.2.0 (#278)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 2.5.2 to 3.2.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](5c56cd6c9d...537aa1903e)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 18:45:22 -04:00
dependabot[bot]
08a9ec380c Bump actions/cache from 2.1.7 to 3.0.8 (#279)
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3.0.8.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](937d244753...fd5de65bc8)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 18:42:48 -04:00
Julien Laffaye
260999f2de Secure GitHub workflows 2022-09-07 18:14:30 -04:00
Julien Laffaye
f1ba13192b Add dependabot for github-actions 2022-09-07 18:03:34 -04:00
Julien Laffaye
9c3c2fd740 Add CodeQL and golangci-lint badges 2022-09-07 18:02:26 -04:00
Julien Laffaye
be12fe0263 Create codeql-analysis.yml 2022-09-07 17:48:18 -04:00
Øyvind Heddeland Instefjord
99be0634ab Add forceListHidden dial option to force the use of 'LIST -a' command (#271)
This is useful for servers that do not offer up
hidden folders/files by default when using LIST/MLSD
commands.
Setting forceListHidden to true will force the use of
the 'LIST -a' command even when MLST support has
been detected.
2022-09-04 14:43:06 -04:00
Julien Laffaye
b85cf1edcc Add default timeout to instantiate connection 2022-08-28 21:58:25 -04:00
Thomas Hallgren
0aeb8660a7 Add MLST command in the form of a Get method (#269)
* Add MLST command in the form of a Get method

The `LIST` and `MLSD` commands are inefficient when the objective
is to retrieve one single `Entry` for a known path, because the only way
to get such an entry is to list the parent directory using a data
connection. The `MLST` fixes this by allowing one single `Entry` to be
returned using the control connection.

The name `Get` was chosen because it is often used in conjunction with
`List` as a mean to get one single entry.

Signed-off-by: Thomas Hallgren <thomas@datawire.io>

* Changes in response to code review:

- Rename `Get` to `GetEntry` (because it returns an `*Entry`)
- Add test-case for multiline response on the control connection
- Fix issues with parsing the multiline response.

Signed-off-by: Thomas Hallgren <thomas@datawire.io>

* Add sample output from MLST to GetEntry comment.

Signed-off-by: Thomas Hallgren <thomas@datawire.io>

* Changes in response to code review:

- Remove unused `time.Time` argument
- Add struct labels to make `govet` happy

Signed-off-by: Thomas Hallgren <thomas@datawire.io>

* Remove time arg when calling parseNextRFC3659ListLine

Signed-off-by: Thomas Hallgren <thomas@datawire.io>

Signed-off-by: Thomas Hallgren <thomas@datawire.io>
2022-08-21 17:25:29 -04:00
Julien Laffaye
4d1d644cf1 Mark Connect() and DialWithTimeout() as deprecated 2022-08-18 12:44:22 -04:00
Julien Laffaye
5a2fd50da8 ioutil is deprecated, use io package instead 2022-08-18 11:18:54 -04:00
Julien Laffaye
9cda78131d Fix transferring spelling. 2022-08-18 10:17:32 -04:00
Julien Laffaye
39592b91e4 Assign type to TransferType constants 2022-08-17 19:43:45 -04:00
Julien Laffaye
fa83b53d0e Use tls.Dialer to be able to use DialContext 2022-08-17 19:35:55 -04:00
Julien Laffaye
45482d097e Use assert package to simplify tests 2022-08-17 19:24:40 -04:00
Julien Laffaye
560423fa8a DialWithNetConn is a special case of DialWithDialFunc 2022-08-17 18:41:24 -04:00
Julien Laffaye
11536801d1 Merge pull request #264 from jlaffaye/dependabot/go_modules/github.com/stretchr/testify-1.8.0
Bump github.com/stretchr/testify from 1.7.2 to 1.8.0
2022-06-30 12:50:35 -04:00
dependabot[bot]
6c74f63d3c Bump github.com/stretchr/testify from 1.7.2 to 1.8.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.2 to 1.8.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.2...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-29 15:39:38 +00:00
Julien Laffaye
60a941566c Merge pull request #259 from jlaffaye/dependabot/go_modules/github.com/stretchr/testify-1.7.2
Bump github.com/stretchr/testify from 1.6.1 to 1.7.2
2022-06-12 11:18:34 -04:00
dependabot[bot]
712e6cb8bc Bump github.com/stretchr/testify from 1.6.1 to 1.7.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.6.1 to 1.7.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.6.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-10 16:37:09 +00:00
Julien Laffaye
6696131620 Create dependabot.yml 2022-06-10 12:36:43 -04:00
Julien Laffaye
dfa1e758f3 Merge pull request #257 from tauu/cmd-type
support for the TYPE command
2022-05-23 20:19:17 -04:00
Georg Wechslberger
b29e1f6c62 refactor: rename TransferTypeImage to TransferTypeBinary 2022-05-23 17:21:05 +02:00
Georg Wechslberger
190f39e8b2 feat: support TYPE command 2022-05-23 12:36:17 +02:00
Julien Laffaye
d2c44e311e Restore previous behavior for List
Fixes #251
2022-03-10 15:20:11 -05:00
14 changed files with 458 additions and 183 deletions

17
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
assignees:
- "jlaffaye"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

69
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '20 19 * * 2'
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: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v4.1.7
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
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.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@64e61baeac852f409b48440cebec029a2d978f90

View File

@@ -5,9 +5,12 @@ jobs:
golangci-lint:
name: lint
runs-on: ubuntu-latest
permissions:
contents: read # for actions/checkout to fetch code
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4.1.7
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86
with:
only-new-issues: true

View File

@@ -5,12 +5,12 @@ jobs:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v4.1.7
- name: Setup go
uses: actions/setup-go@v2
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32
with:
go-version: 1.17
- uses: actions/cache@v2
go-version: 1.19
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
with:
path: |
~/go/pkg/mod
@@ -21,9 +21,9 @@ jobs:
- name: Run tests
run: go test -v -covermode=count -coverprofile=coverage.out
- name: Convert coverage to lcov
uses: jandelgado/gcov2lcov-action@v1.0.8
uses: jandelgado/gcov2lcov-action@c680c0f7c7442485f1749eb2a13e54a686e76eb5
- name: Coveralls
uses: coverallsapp/github-action@v1.1.2
uses: coverallsapp/github-action@cfd0633edbd2411b532b808ba7a8b5e04f76d2c8
with:
github-token: ${{ secrets.github_token }}
path-to-lcov: coverage.lcov

View File

@@ -2,6 +2,8 @@
[![Units tests](https://github.com/jlaffaye/ftp/actions/workflows/unit_tests.yaml/badge.svg)](https://github.com/jlaffaye/ftp/actions/workflows/unit_tests.yaml)
[![Coverage Status](https://coveralls.io/repos/jlaffaye/ftp/badge.svg?branch=master&service=github)](https://coveralls.io/github/jlaffaye/ftp?branch=master)
[![golangci-lint](https://github.com/jlaffaye/ftp/actions/workflows/golangci-lint.yaml/badge.svg)](https://github.com/jlaffaye/ftp/actions/workflows/golangci-lint.yaml)
[![CodeQL](https://github.com/jlaffaye/ftp/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/jlaffaye/ftp/actions/workflows/codeql-analysis.yml)
[![Go ReportCard](https://goreportcard.com/badge/jlaffaye/ftp)](http://goreportcard.com/report/jlaffaye/ftp)
[![Go Reference](https://pkg.go.dev/badge/github.com/jlaffaye/ftp.svg)](https://pkg.go.dev/github.com/jlaffaye/ftp)

View File

@@ -3,10 +3,8 @@ package ftp
import (
"bytes"
"fmt"
"io/ioutil"
"io"
"net"
"net/textproto"
"strings"
"syscall"
"testing"
"time"
@@ -28,174 +26,145 @@ func TestConnEPSV(t *testing.T) {
}
func testConn(t *testing.T, disableEPSV bool) {
assert := assert.New(t)
mock, c := openConn(t, "127.0.0.1", DialWithTimeout(5*time.Second), DialWithDisabledEPSV(disableEPSV))
err := c.Login("anonymous", "anonymous")
if err != nil {
t.Fatal(err)
}
assert.NoError(err)
err = c.NoOp()
if err != nil {
t.Error(err)
}
assert.NoError(err)
err = c.ChangeDir("incoming")
if err != nil {
t.Error(err)
}
assert.NoError(err)
dir, err := c.CurrentDir()
if err != nil {
t.Error(err)
} else {
if dir != "/incoming" {
t.Error("Wrong dir: " + dir)
}
if assert.NoError(err) {
assert.Equal("/incoming", dir)
}
data := bytes.NewBufferString(testData)
err = c.Stor("test", data)
if err != nil {
t.Error(err)
}
assert.NoError(err)
_, err = c.List(".")
if err != nil {
t.Error(err)
}
assert.NoError(err)
err = c.Rename("test", "tset")
if err != nil {
t.Error(err)
}
assert.NoError(err)
// Read without deadline
r, err := c.Retr("tset")
if err != nil {
t.Error(err)
} else {
buf, errRead := ioutil.ReadAll(r)
if err != nil {
t.Error(errRead)
}
if string(buf) != testData {
t.Errorf("'%s'", buf)
if assert.NoError(err) {
buf, err := io.ReadAll(r)
if assert.NoError(err) {
assert.Equal(testData, string(buf))
}
r.Close()
r.Close() // test we can close two times
}
// Read with deadline
r, err = c.Retr("tset")
if err != nil {
t.Error(err)
} else {
if assert.NoError(err) {
if err := r.SetDeadline(time.Now()); err != nil {
t.Fatal(err)
}
_, err = ioutil.ReadAll(r)
if err == nil {
t.Error("deadline should have caused error")
} else if !strings.HasSuffix(err.Error(), "i/o timeout") {
t.Error(err)
}
_, err = io.ReadAll(r)
assert.ErrorContains(err, "i/o timeout")
r.Close()
}
// Read with offset
r, err = c.RetrFrom("tset", 5)
if err != nil {
t.Error(err)
} else {
buf, errRead := ioutil.ReadAll(r)
if errRead != nil {
t.Error(errRead)
}
if assert.NoError(err) {
buf, err := io.ReadAll(r)
if assert.NoError(err) {
expected := testData[5:]
if string(buf) != expected {
t.Errorf("read %q, expected %q", buf, expected)
assert.Equal(expected, string(buf))
}
r.Close()
}
data2 := bytes.NewBufferString(testData)
err = c.Append("tset", data2)
if err != nil {
t.Error(err)
}
assert.NoError(err)
// Read without deadline, after append
r, err = c.Retr("tset")
if err != nil {
t.Error(err)
} else {
buf, errRead := ioutil.ReadAll(r)
if err != nil {
t.Error(errRead)
}
if string(buf) != testData+testData {
t.Errorf("'%s'", buf)
if assert.NoError(err) {
buf, err := io.ReadAll(r)
if assert.NoError(err) {
assert.Equal(testData+testData, string(buf))
}
r.Close()
}
fileSize, err := c.FileSize("magic-file")
assert.NoError(err)
assert.Equal(int64(42), fileSize)
_, err = c.FileSize("not-found")
assert.Error(err)
entry, err := c.GetEntry("magic-file")
if err != nil {
t.Error(err)
}
if fileSize != 42 {
t.Errorf("file size %q, expected %q", fileSize, 42)
if entry == nil {
t.Fatal("expected entry, got nil")
}
if entry.Size != 42 {
t.Errorf("entry size %q, expected %q", entry.Size, 42)
}
if entry.Type != EntryTypeFile {
t.Errorf("entry type %q, expected %q", entry.Type, EntryTypeFile)
}
if entry.Name != "magic-file" {
t.Errorf("entry name %q, expected %q", entry.Name, "magic-file")
}
_, err = c.FileSize("not-found")
if err == nil {
t.Fatal("expected error, got nil")
entry, err = c.GetEntry("multiline-dir")
if err != nil {
t.Error(err)
}
if entry == nil {
t.Fatal("expected entry, got nil")
}
if entry.Size != 0 {
t.Errorf("entry size %q, expected %q", entry.Size, 0)
}
if entry.Type != EntryTypeFolder {
t.Errorf("entry type %q, expected %q", entry.Type, EntryTypeFolder)
}
if entry.Name != "multiline-dir" {
t.Errorf("entry name %q, expected %q", entry.Name, "multiline-dir")
}
err = c.Delete("tset")
if err != nil {
t.Error(err)
}
assert.NoError(err)
err = c.MakeDir(testDir)
if err != nil {
t.Error(err)
}
assert.NoError(err)
err = c.ChangeDir(testDir)
if err != nil {
t.Error(err)
}
assert.NoError(err)
err = c.ChangeDirToParent()
if err != nil {
t.Error(err)
}
assert.NoError(err)
entries, err := c.NameList("/")
if err != nil {
t.Error(err)
}
if len(entries) != 1 || entries[0] != "/incoming" {
t.Errorf("Unexpected entries: %v", entries)
}
assert.NoError(err)
assert.Equal([]string{"/incoming"}, entries)
err = c.RemoveDir(testDir)
if err != nil {
t.Error(err)
}
assert.NoError(err)
err = c.Logout()
if err != nil {
if protoErr := err.(*textproto.Error); protoErr != nil {
if protoErr.Code != StatusNotImplemented {
t.Error(err)
}
} else {
t.Error(err)
}
}
assert.NoError(err)
if err = c.Quit(); err != nil {
t.Fatal(err)
@@ -205,9 +174,7 @@ func testConn(t *testing.T, disableEPSV bool) {
mock.Wait()
err = c.NoOp()
if err == nil {
t.Error("Expected error")
}
assert.Error(err, "should error on closed conn")
}
// TestConnect tests the legacy Connect function
@@ -312,7 +279,7 @@ func TestMissingFolderDeleteDirRecur(t *testing.T) {
}
func TestListCurrentDir(t *testing.T) {
mock, c := openConn(t, "127.0.0.1")
mock, c := openConnExt(t, "127.0.0.1", "no-time", DialWithDisabledMLSD(true))
_, err := c.List("")
assert.NoError(t, err)
@@ -328,6 +295,20 @@ func TestListCurrentDir(t *testing.T) {
mock.Wait()
}
func TestListCurrentDirWithForceListHidden(t *testing.T) {
mock, c := openConnExt(t, "127.0.0.1", "no-time", DialWithDisabledMLSD(true), DialWithForceListHidden(true))
assert.True(t, c.options.forceListHidden)
_, err := c.List("")
assert.NoError(t, err)
assert.Equal(t, "LIST -a", mock.lastFull, "LIST -a must not have a trailing whitespace")
err = c.Quit()
assert.NoError(t, err)
mock.Wait()
}
func TestTimeUnsupported(t *testing.T) {
mock, c := openConnExt(t, "127.0.0.1", "no-time")

View File

@@ -6,12 +6,14 @@ import (
"io"
"net"
"net/textproto"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type ftpMock struct {
@@ -88,7 +90,7 @@ func (mock *ftpMock) listen() {
// At least one command must have a multiline response
switch cmdParts[0] {
case "FEAT":
features := "211-Features:\r\n FEAT\r\n PASV\r\n EPSV\r\n UTF8\r\n SIZE\r\n"
features := "211-Features:\r\n FEAT\r\n PASV\r\n EPSV\r\n UTF8\r\n SIZE\r\n MLST\r\n"
switch mock.modtime {
case "std-time":
features += " MDTM\r\n MFMT\r\n"
@@ -173,9 +175,26 @@ func (mock *ftpMock) listen() {
mock.dataConn.Wait()
mock.printfLine("150 Opening ASCII mode data connection for file list")
mock.dataConn.write([]byte("-rw-r--r-- 1 ftp wheel 0 Jan 29 10:29 lo"))
mock.dataConn.write([]byte("-rw-r--r-- 1 ftp wheel 0 Jan 29 10:29 lo\r\ntotal 1"))
mock.printfLine("226 Transfer complete")
mock.closeDataConn()
case "MLSD":
if mock.dataConn == nil {
mock.printfLine("425 Unable to build data connection: Connection refused")
break
}
mock.dataConn.Wait()
mock.printfLine("150 Opening data connection for file list")
mock.dataConn.write([]byte("Type=file;Size=0;Modify=20201213202400; lo\r\n"))
mock.printfLine("226 Transfer complete")
mock.closeDataConn()
case "MLST":
if cmdParts[1] == "multiline-dir" {
mock.printfLine("250-File data\r\n Type=dir;Size=0; multiline-dir\r\n Modify=20201213202400; multiline-dir\r\n250 End")
} else {
mock.printfLine("250-File data\r\n Type=file;Size=42;Modify=20201213202400; magic-file\r\n \r\n250 End")
}
case "NLST":
if mock.dataConn == nil {
mock.printfLine("425 Unable to build data connection: Connection refused")
@@ -386,20 +405,14 @@ func openConn(t *testing.T, addr string, options ...DialOption) (*ftpMock, *Serv
func openConnExt(t *testing.T, addr, modtime string, options ...DialOption) (*ftpMock, *ServerConn) {
mock, err := newFtpMockExt(t, addr, modtime)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
defer mock.Close()
c, err := Dial(mock.Addr(), options...)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
err = c.Login("anonymous", "anonymous")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
return mock, c
}
@@ -417,9 +430,7 @@ func closeConn(t *testing.T, mock *ftpMock, c *ServerConn, commands []string) {
// Wait for the connection to close
mock.Wait()
if !reflect.DeepEqual(mock.commands, expected) {
t.Fatal("unexpected sequence of commands:", mock.commands, "expected:", expected)
}
assert.Equal(t, expected, mock.commands, "unexpected sequence of commands")
}
func TestConn4(t *testing.T) {

200
ftp.go
View File

@@ -18,6 +18,12 @@ import (
"github.com/hashicorp/go-multierror"
)
const (
// 30 seconds was chosen as it's the
// same duration as http.DefaultTransport's timeout.
DefaultDialTimeout = 30 * time.Second
)
// EntryType describes the different types of an Entry.
type EntryType int
@@ -28,6 +34,15 @@ const (
EntryTypeLink
)
// TransferType denotes the formats for transferring Entries.
type TransferType string
// The different transfer types
const (
TransferTypeBinary = TransferType("I")
TransferTypeASCII = TransferType("A")
)
// Time format used by the MDTM and MFMT commands
const timeFormat = "20060102150405"
@@ -61,11 +76,11 @@ type dialOptions struct {
dialer net.Dialer
tlsConfig *tls.Config
explicitTLS bool
conn net.Conn
disableEPSV bool
disableUTF8 bool
disableMLSD bool
writingMDTM bool
forceListHidden bool
location *time.Location
debugOutput io.Writer
dialFunc func(network, address string) (net.Conn, error)
@@ -99,28 +114,40 @@ func Dial(addr string, options ...DialOption) (*ServerConn, error) {
do.location = time.UTC
}
tconn := do.conn
if tconn == nil {
var err error
dialFunc := do.dialFunc
if do.dialFunc != nil {
tconn, err = do.dialFunc("tcp", addr)
} else if do.tlsConfig != nil && !do.explicitTLS {
tconn, err = tls.DialWithDialer(&do.dialer, "tcp", addr, do.tlsConfig)
} else {
if dialFunc == nil {
ctx := do.context
if ctx == nil {
ctx = context.Background()
}
tconn, err = do.dialer.DialContext(ctx, "tcp", addr)
if _, ok := ctx.Deadline(); !ok {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, DefaultDialTimeout)
defer cancel()
}
if do.tlsConfig != nil && !do.explicitTLS {
dialFunc = func(network, address string) (net.Conn, error) {
tlsDialer := &tls.Dialer{
NetDialer: &do.dialer,
Config: do.tlsConfig,
}
return tlsDialer.DialContext(ctx, network, addr)
}
} else {
dialFunc = func(network, address string) (net.Conn, error) {
return do.dialer.DialContext(ctx, network, addr)
}
}
}
tconn, err := dialFunc("tcp", addr)
if err != nil {
return nil, err
}
}
// Use the resolved IP address in case addr contains a domain name
// If we use the domain name, we might not resolve to the same IP.
@@ -134,7 +161,7 @@ func Dial(addr string, options ...DialOption) (*ServerConn, error) {
host: remoteAddr.IP.String(),
}
_, _, err := c.conn.ReadResponse(StatusReady)
_, _, err = c.conn.ReadResponse(StatusReady)
if err != nil {
_ = c.Quit()
return nil, err
@@ -176,10 +203,12 @@ func DialWithDialer(dialer net.Dialer) DialOption {
}
// DialWithNetConn returns a DialOption that configures the ServerConn with the underlying net.Conn
//
// Deprecated: Use [DialWithDialFunc] instead
func DialWithNetConn(conn net.Conn) DialOption {
return DialOption{func(do *dialOptions) {
do.conn = conn
}}
return DialWithDialFunc(func(network, address string) (net.Conn, error) {
return conn, nil
})
}
// DialWithDisabledEPSV returns a DialOption that configures the ServerConn with EPSV disabled
@@ -219,6 +248,16 @@ func DialWithWritingMDTM(enabled bool) DialOption {
}}
}
// DialWithForceListHidden returns a DialOption making ServerConn use LIST -a to include hidden files and folders in directory listings
//
// This is useful for servers that do not do this by default, but it forces the use of the LIST command
// even if the server supports MLST.
func DialWithForceListHidden(enabled bool) DialOption {
return DialOption{func(do *dialOptions) {
do.forceListHidden = enabled
}}
}
// DialWithLocation returns a DialOption that configures the ServerConn with specified time.Location
// The location is used to parse the dates sent by the server which are in server's timezone
func DialWithLocation(location *time.Location) DialOption {
@@ -292,14 +331,15 @@ func (o *dialOptions) wrapStream(rd io.ReadCloser) io.ReadCloser {
}
// Connect is an alias to Dial, for backward compatibility
//
// Deprecated: Use [Dial] instead
func Connect(addr string) (*ServerConn, error) {
return Dial(addr)
}
// DialTimeout initializes the connection to the specified ftp server address.
//
// It is generally followed by a call to Login() as most FTP commands require
// an authenticated user.
// Deprecated: Use [Dial] with [DialWithTimeout] option instead
func DialTimeout(addr string, timeout time.Duration) (*ServerConn, error) {
return Dial(addr, DialWithTimeout(timeout))
}
@@ -340,7 +380,7 @@ func (c *ServerConn) Login(user, password string) error {
c.mdtmCanWrite = c.mdtmSupported && c.options.writingMDTM
// Switch to binary mode
if _, _, err = c.cmd(StatusCommandOK, "TYPE I"); err != nil {
if err = c.Type(TransferTypeBinary); err != nil {
return err
}
@@ -488,9 +528,26 @@ func (c *ServerConn) pasv() (host string, port int, err error) {
// Make the IP address to connect to
host = strings.Join(pasvData[0:4], ".")
if c.host != host {
if cmdIP := net.ParseIP(c.host); cmdIP != nil {
if dataIP := net.ParseIP(host); dataIP != nil {
if isBogusDataIP(cmdIP, dataIP) {
return c.host, port, nil
}
}
}
}
return host, port, nil
}
func isBogusDataIP(cmdIP, dataIP net.IP) bool {
// Logic stolen from lftp (https://github.com/lavv17/lftp/blob/d67fc14d085849a6b0418bb3e912fea2e94c18d1/src/ftpclass.cc#L769)
return dataIP.IsMulticast() ||
cmdIP.IsPrivate() != dataIP.IsPrivate() ||
cmdIP.IsLoopback() != dataIP.IsLoopback()
}
// getDataConnPort returns a host, port for a new data connection
// it uses the best available method to do so
func (c *ServerConn) getDataConnPort() (string, int, error) {
@@ -519,7 +576,24 @@ func (c *ServerConn) openDataConn() (net.Conn, error) {
}
if c.options.tlsConfig != nil {
return tls.DialWithDialer(&c.options.dialer, "tcp", addr, c.options.tlsConfig)
// We don't use tls.DialWithDialer here (which does Dial, create
// the Client and then do the Handshake) because it seems to
// hang with some FTP servers, namely proftpd and pureftpd.
//
// Instead we do Dial, create the Client and wait for the first
// Read or Write to trigger the Handshake.
//
// This means that if we are uploading a zero sized file, we
// need to make sure we do the Handshake explicitly as Write
// won't have been called. This is done in StorFrom().
//
// See: https://github.com/jlaffaye/ftp/issues/282
conn, err := c.options.dialer.Dial("tcp", addr)
if err != nil {
return nil, err
}
tlsConn := tls.Client(conn, c.options.tlsConfig)
return tlsConn, nil
}
return c.options.dialer.Dial("tcp", addr)
@@ -580,6 +654,12 @@ func (c *ServerConn) cmdDataConnFrom(offset uint64, format string, args ...inter
return conn, nil
}
// Type switches the transfer mode for the connection.
func (c *ServerConn) Type(transferType TransferType) (err error) {
_, _, err = c.cmd(StatusCommandOK, "TYPE "+string(transferType))
return err
}
// NameList issues an NLST FTP command.
func (c *ServerConn) NameList(path string) (entries []string, err error) {
space := " "
@@ -615,11 +695,14 @@ func (c *ServerConn) List(path string) (entries []*Entry, err error) {
var cmd string
var parser parseFunc
if c.mlstSupported {
if c.mlstSupported && !c.options.forceListHidden {
cmd = "MLSD"
parser = parseRFC3659ListLine
} else {
cmd = "LIST"
if c.options.forceListHidden {
cmd += " -a"
}
parser = parseListLine
}
@@ -640,9 +723,7 @@ func (c *ServerConn) List(path string) (entries []*Entry, err error) {
now := time.Now()
for scanner.Scan() {
entry, errParse := parser(scanner.Text(), now, c.options.location)
if errParse != nil {
errs = multierror.Append(errs, errParse)
} else {
if errParse == nil {
entries = append(entries, entry)
}
}
@@ -657,6 +738,62 @@ func (c *ServerConn) List(path string) (entries []*Entry, err error) {
return entries, errs.ErrorOrNil()
}
// GetEntry issues a MLST FTP command which retrieves one single Entry using the
// control connection. The returnedEntry will describe the current directory
// when no path is given.
func (c *ServerConn) GetEntry(path string) (entry *Entry, err error) {
if !c.mlstSupported {
return nil, &textproto.Error{Code: StatusNotImplemented, Msg: StatusText(StatusNotImplemented)}
}
space := " "
if path == "" {
space = ""
}
_, msg, err := c.cmd(StatusRequestedFileActionOK, "%s%s%s", "MLST", space, path)
if err != nil {
return nil, err
}
// The expected reply will look something like:
//
// 250-File details
// Type=file;Size=1024;Modify=20220813133357; path
// 250 End
//
// Multiple lines are allowed though, so it can also be in the form:
//
// 250-File details
// Type=file;Size=1024; path
// Modify=20220813133357; path
// 250 End
lines := strings.Split(msg, "\n")
lc := len(lines)
// lines must be a multi-line message with a length of 3 or more, and we
// don't care about the first and last line
if lc < 3 {
return nil, errors.New("invalid response")
}
e := &Entry{}
for _, l := range lines[1 : lc-1] {
// According to RFC 3659, the entry lines must start with a space when passed over the
// control connection. Some servers don't seem to add that space though. Both forms are
// accepted here.
if len(l) > 0 && l[0] == ' ' {
l = l[1:]
}
// Some severs seem to send a blank line at the end which we ignore
if l == "" {
continue
}
if e, err = parseNextRFC3659ListLine(l, c.options.location, e); err != nil {
return nil, err
}
}
return e, nil
}
// IsTimePreciseInList returns true if client and server support the MLSD
// command so List can return time with 1-second precision for all files.
func (c *ServerConn) IsTimePreciseInList() bool {
@@ -813,8 +950,21 @@ func (c *ServerConn) StorFrom(path string, r io.Reader, offset uint64) error {
// response otherwise if the failure is not due to a connection problem,
// for example the server denied the upload for quota limits, we miss
// the response and we cannot use the connection to send other commands.
if _, err := io.Copy(conn, r); err != nil {
if n, err := io.Copy(conn, r); err != nil {
errs = multierror.Append(errs, err)
} else if n == 0 {
// If we wrote no bytes and got no error, make sure we call
// tls.Handshake on the connection as it won't get called
// unless Write() is called. (See comment in openDataConn()).
//
// ProFTP doesn't like this and returns "Unable to build data
// connection: Operation not permitted" when trying to upload
// an empty file without this.
if do, ok := conn.(interface{ Handshake() error }); ok {
if err := do.Handshake(); err != nil {
errs = multierror.Append(errs, err)
}
}
}
if err := conn.Close(); err != nil {

22
ftp_test.go Normal file
View File

@@ -0,0 +1,22 @@
package ftp
import (
"net"
"testing"
)
func TestBogusDataIP(t *testing.T) {
for _, tC := range []struct {
cmd, data net.IP
bogus bool
}{
{net.IPv4(192, 168, 1, 1), net.IPv4(192, 168, 1, 1), false},
{net.IPv4(192, 168, 1, 1), net.IPv4(1, 1, 1, 1), true},
{net.IPv4(10, 65, 1, 1), net.IPv4(1, 1, 1, 1), true},
{net.IPv4(10, 65, 25, 1), net.IPv4(10, 65, 8, 1), false},
} {
if got, want := isBogusDataIP(tC.cmd, tC.data), tC.bogus; got != want {
t.Errorf("%s,%s got %t, wanted %t", tC.cmd, tC.data, got, want)
}
}
}

6
go.mod
View File

@@ -4,12 +4,12 @@ go 1.17
require (
github.com/hashicorp/go-multierror v1.1.1
github.com/stretchr/testify v1.6.1
github.com/stretchr/testify v1.9.0
)
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

16
go.sum
View File

@@ -1,5 +1,6 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
@@ -7,9 +8,16 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -24,10 +24,16 @@ var listLineParsers = []parseFunc{
var dirTimeFormats = []string{
"01-02-06 03:04PM",
"2006-01-02 15:04",
"01-02-2006 03:04PM",
"01-02-2006 15:04",
}
// parseRFC3659ListLine parses the style of directory line defined in RFC 3659.
func parseRFC3659ListLine(line string, now time.Time, loc *time.Location) (*Entry, error) {
func parseRFC3659ListLine(line string, _ time.Time, loc *time.Location) (*Entry, error) {
return parseNextRFC3659ListLine(line, loc, &Entry{})
}
func parseNextRFC3659ListLine(line string, loc *time.Location, e *Entry) (*Entry, error) {
iSemicolon := strings.Index(line, ";")
iWhitespace := strings.Index(line, " ")
@@ -35,8 +41,12 @@ func parseRFC3659ListLine(line string, now time.Time, loc *time.Location) (*Entr
return nil, errUnsupportedListLine
}
e := &Entry{
Name: line[iWhitespace+1:],
name := line[iWhitespace+1:]
if e.Name == "" {
e.Name = name
} else if e.Name != name {
// All lines must have the same name
return nil, errUnsupportedListLine
}
for _, field := range strings.Split(line[:iWhitespace-1], ";") {

View File

@@ -68,6 +68,8 @@ var listTests = []line{
// DOS DIR command output
{"08-07-15 07:50PM 718 Post_PRR_20150901_1166_265118_13049.dat", "Post_PRR_20150901_1166_265118_13049.dat", 718, EntryTypeFile, newTime(2015, time.August, 7, 19, 50)},
{"08-10-15 02:04PM <DIR> Billing", "Billing", 0, EntryTypeFolder, newTime(2015, time.August, 10, 14, 4)},
{"08-07-2015 07:50PM 718 Post_PRR_20150901_1166_265118_13049.dat", "Post_PRR_20150901_1166_265118_13049.dat", 718, EntryTypeFile, newTime(2015, time.August, 7, 19, 50)},
{"08-10-2015 02:04PM <DIR> Billing", "Billing", 0, EntryTypeFolder, newTime(2015, time.August, 10, 14, 4)},
// dir and file names that contain multiple spaces
{"drwxr-xr-x 3 110 1002 3 Dec 02 2009 spaces dir name", "spaces dir name", 0, EntryTypeFolder, newTime(2009, time.December, 2)},