Skip to content

Commit

Permalink
Add FTP integration
Browse files Browse the repository at this point in the history
  • Loading branch information
nemunaire committed Aug 18, 2023
1 parent 9efc31c commit ed3322f
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 7 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ require (
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 // indirect
github.com/stretchr/testify v1.8.3 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnH
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo=
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4/go.mod h1:MnkX001NG75g3p8bhFycnyIjeQoOjGL6CEIsdE/nKSY=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
Expand Down
126 changes: 126 additions & 0 deletions internal/integrations/ftp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package integrations

import (
"bytes"
"io"
"os"
"path"
"strings"

"github.com/ddvk/rmfakecloud/internal/messages"
"github.com/ddvk/rmfakecloud/internal/model"
"github.com/secsy/goftp"
"github.com/sirupsen/logrus"
)

type FTPIntegration struct {
client *goftp.Client
}

func newFTP(i model.IntegrationConfig) *FTPIntegration {
config := goftp.Config{
Logger: os.Stderr,
ActiveTransfers: i.ActiveTransfers,
}

if i.Username != "" {
config.User = i.Username
}
if i.Password != "" {
config.Password = i.Password
}

client, err := goftp.DialConfig(config, strings.TrimPrefix(i.Address, "ftp://"))
if err != nil {
logrus.Errorf("An error occurred creating FTP client: %v\n", err)
return nil
}

return &FTPIntegration{
client,
}
}

func (g *FTPIntegration) List(folder string, depth int) (*messages.IntegrationFolder, error) {
response := messages.NewIntegrationFolder(folder, "")

if folder == rootFolder {
folder = "/"
response.Name = "FTP root"
} else {
decoded, err := decodeName(folder)
if err != nil {
return nil, err
}
folder = decoded
response.Name = path.Base(folder)
}
logrus.Info("[ftp] query for: ", folder, " depth: ", depth)

err := visitDir("", folder, depth, response, g.client.ReadDir)
if err != nil {
return nil, err
}

return response, nil
}

func (g *FTPIntegration) GetMetadata(fileID string) (*messages.IntegrationMetadata, error) {
decoded, err := decodeName(fileID)
if err != nil {
return nil, err
}

ext := path.Ext(decoded)
contentType := contentTypeFromExt(ext)

return &messages.IntegrationMetadata{
ID: fileID,
Name: path.Base(decoded),
Thumbnail: []byte{},
SourceFileType: contentType,
ProvidedFileType: contentType,
FileType: ext,
}, nil
}

func (g *FTPIntegration) Download(fileID string) (io.ReadCloser, int64, error) {
decoded, err := decodeName(fileID)
if err != nil {
return nil, 0, err
}

st, err := g.client.Stat(decoded)
if err != nil {
return nil, 0, err
}

var buf bytes.Buffer

err = g.client.Retrieve(decoded, &buf)
if err != nil {
return nil, st.Size(), err
}

return io.NopCloser(&buf), st.Size(), err
}

func (g *FTPIntegration) Upload(folderID, name, fileType string, reader io.ReadCloser) (id string, err error) {
folder := "/"
if folderID != rootFolder {
folder, err = decodeName(folderID)
if err != nil {
return
}
}
fullpath := path.Join(folder, name+"."+fileType)
logrus.Trace(logger, "Uploading: ", fullpath)

err = g.client.Store(fullpath, reader)

if err != nil {
return
}
id = encodeName(fullpath)
return
}
5 changes: 5 additions & 0 deletions internal/integrations/integrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
)

const (
ftpProvider = "ftp"
webdavProvider = "webdav"
dropboxProvider = "dropbox"
googleProvider = "google"
Expand Down Expand Up @@ -40,6 +41,8 @@ func GetIntegrationProvider(storer storage.UserStorer, uid, integrationid string
switch intg.Provider {
case dropboxProvider:
return newDropbox(intg), nil
case ftpProvider:
return newFTP(intg), nil
case localfsProvider:
return newLocalFS(intg), nil
case webdavProvider:
Expand All @@ -53,6 +56,8 @@ func GetIntegrationProvider(storer storage.UserStorer, uid, integrationid string
// fix the name
func fixProviderName(n string) string {
switch n {
case ftpProvider:
fallthrough
case dropboxProvider:
return "Dropbox"
case googleProvider:
Expand Down
5 changes: 4 additions & 1 deletion internal/model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ type IntegrationConfig struct {
Provider string
Name string

// WebDav
// WebDav // FTP
Username string
Password string
Address string

// FTP
ActiveTransfers bool

// Insecure ignore TLS cert errors
Insecure bool

Expand Down
18 changes: 15 additions & 3 deletions ui/src/components/IntegrationModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function IntegrationModal(params) {
username: integration?.Username,
password: integration?.Password,
address: integration?.Address,
activetransfers: integration?.ActiveTransfers,
insecure: integration?.Insecure,
accesstoken: integration?.Accesstoken,
path: integration?.Path,
Expand Down Expand Up @@ -48,6 +49,7 @@ export default function IntegrationModal(params) {
username: integrationForm.username,
password: integrationForm.password,
address: integrationForm.address,
activetransfers: integrationForm?.activetransfers,
insecure: integrationForm.insecure,
accesstoken: integrationForm.accesstoken,
path: integrationForm.path,
Expand Down Expand Up @@ -89,6 +91,7 @@ export default function IntegrationModal(params) {
>
<option value="localfs">Directory in file system</option>
<option value="webdav">WebDAV</option>
<option value="ftp">FTP</option>
<option value="dropbox">Dropbox</option>
</Form.Control>

Expand All @@ -100,7 +103,7 @@ export default function IntegrationModal(params) {
onChange={handleChange}
/>

{integrationForm.provider === "webdav" && (
{(integrationForm.provider === "webdav" || integrationForm.provider === "ftp") && (
<>
<Form.Label>Address</Form.Label>
<Form.Control
Expand All @@ -111,7 +114,7 @@ export default function IntegrationModal(params) {
/>
</>
)}
{integrationForm.provider === "webdav" && (
{(integrationForm.provider === "webdav" || integrationForm.provider === "ftp") && (
<>
<Form.Label>Username</Form.Label>
<Form.Control
Expand All @@ -122,7 +125,7 @@ export default function IntegrationModal(params) {
/>
</>
)}
{integrationForm.provider === "webdav" && (
{(integrationForm.provider === "webdav" || integrationForm.provider === "ftp") && (
<>
<Form.Label>Password</Form.Label>
<Form.Control
Expand All @@ -135,6 +138,15 @@ export default function IntegrationModal(params) {
</>
)}

{integrationForm.provider === "ftp" && (
<Form.Check
name="activetransfers"
checked={integrationForm.activetransfers}
onChange={({ target }) => setIntegrationForm({ ...integrationForm, [target.name]: target.checked })}
label="Use actives transfers"
/>
)}

{integrationForm.provider === "localfs" && (
<>
<Form.Label>Path</Form.Label>
Expand Down
16 changes: 13 additions & 3 deletions ui/src/components/NewIntegrationModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ export default function IntegrationProfileModal(params) {
onChange={handleChange}
>
<option value="localfs">Directory in file system</option>
<option value="ftp">FTP</option>
<option value="webdav">WebDAV</option>
<option value="dropbox">Dropbox</option>
</Form.Control>

{integrationForm.provider === "webdav" && (
{(integrationForm.provider === "webdav" || integrationForm.provider === "ftp") && (
<>
<Form.Label>Address</Form.Label>
<Form.Control
Expand All @@ -93,7 +94,7 @@ export default function IntegrationProfileModal(params) {
/>
</>
)}
{integrationForm.provider === "webdav" && (
{(integrationForm.provider === "webdav" || integrationForm.provider === "ftp") && (
<>
<Form.Label>Username</Form.Label>
<Form.Control
Expand All @@ -104,7 +105,7 @@ export default function IntegrationProfileModal(params) {
/>
</>
)}
{integrationForm.provider === "webdav" && (
{(integrationForm.provider === "webdav" || integrationForm.provider === "ftp") && (
<>
<Form.Label>Password</Form.Label>
<Form.Control
Expand All @@ -117,6 +118,15 @@ export default function IntegrationProfileModal(params) {
</>
)}

{integrationForm.provider === "ftp" && (
<Form.Check
name="activetransfers"
checked={integrationForm.activetransfers}
onChange={({ target }) => setIntegrationForm({ ...integrationForm, [target.name]: target.checked })}
label="Use actives transfers"
/>
)}

{integrationForm.provider === "localfs" && (
<>
<Form.Label>Path</Form.Label>
Expand Down

0 comments on commit ed3322f

Please sign in to comment.