mirror of
https://github.com/rclone/rclone.git
synced 2025-03-14 08:25:15 +08:00
link: allow creating public link to files and folders - closes #1562
This commit is contained in:
parent
9df266a6b4
commit
a8267d1628
@ -71,6 +71,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -71,6 +71,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
1
backend/cache/cache_test.go
vendored
1
backend/cache/cache_test.go
vendored
@ -72,6 +72,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -69,6 +69,7 @@ func TestObjectUpdate2(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable2(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable2(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile2(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile2(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound2(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound2(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink2(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove2(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove2(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream2(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream2(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge2(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge2(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -69,6 +69,7 @@ func TestObjectUpdate3(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable3(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable3(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile3(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile3(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound3(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound3(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink3(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove3(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove3(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream3(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream3(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge3(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge3(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -69,6 +69,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -1095,6 +1095,41 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) {
|
|||||||
return dstObj, nil
|
return dstObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicLink adds a "readable by anyone with link" permission on the given file or folder.
|
||||||
|
func (f *Fs) PublicLink(remote string) (link string, err error) {
|
||||||
|
id, err := f.dirCache.FindDir(remote, false)
|
||||||
|
if err == nil {
|
||||||
|
fs.Debugf(f, "attempting to share directory '%s'", remote)
|
||||||
|
} else {
|
||||||
|
fs.Debugf(f, "attempting to share single file '%s'", remote)
|
||||||
|
o := &Object{
|
||||||
|
fs: f,
|
||||||
|
remote: remote,
|
||||||
|
}
|
||||||
|
if err = o.readMetaData(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id = o.id
|
||||||
|
}
|
||||||
|
|
||||||
|
permission := &drive.Permission{
|
||||||
|
AllowFileDiscovery: false,
|
||||||
|
Role: "reader",
|
||||||
|
Type: "anyone",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
// TODO: On TeamDrives this might fail if lacking permissions to change ACLs.
|
||||||
|
// Need to either check `canShare` attribute on the object or see if a sufficient permission is already present.
|
||||||
|
_, err = f.svc.Permissions.Create(id, permission).Fields(googleapi.Field("id")).SupportsTeamDrives(f.isTeamDrive).Do()
|
||||||
|
return shouldRetry(err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("https://drive.google.com/open?id=%s", id), nil
|
||||||
|
}
|
||||||
|
|
||||||
// DirMove moves src, srcRemote to this remote at dstRemote
|
// DirMove moves src, srcRemote to this remote at dstRemote
|
||||||
// using server side move operations.
|
// using server side move operations.
|
||||||
//
|
//
|
||||||
@ -1597,6 +1632,7 @@ var (
|
|||||||
_ fs.DirCacheFlusher = (*Fs)(nil)
|
_ fs.DirCacheFlusher = (*Fs)(nil)
|
||||||
_ fs.ChangeNotifier = (*Fs)(nil)
|
_ fs.ChangeNotifier = (*Fs)(nil)
|
||||||
_ fs.PutUncheckeder = (*Fs)(nil)
|
_ fs.PutUncheckeder = (*Fs)(nil)
|
||||||
|
_ fs.PublicLinker = (*Fs)(nil)
|
||||||
_ fs.MergeDirser = (*Fs)(nil)
|
_ fs.MergeDirser = (*Fs)(nil)
|
||||||
_ fs.Object = (*Object)(nil)
|
_ fs.Object = (*Object)(nil)
|
||||||
_ fs.MimeTyper = &Object{}
|
_ fs.MimeTyper = &Object{}
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -36,6 +36,7 @@ import (
|
|||||||
|
|
||||||
"github.com/dropbox/dropbox-sdk-go-unofficial/dropbox"
|
"github.com/dropbox/dropbox-sdk-go-unofficial/dropbox"
|
||||||
"github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/files"
|
"github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/files"
|
||||||
|
"github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/sharing"
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
"github.com/ncw/rclone/fs/config"
|
"github.com/ncw/rclone/fs/config"
|
||||||
"github.com/ncw/rclone/fs/config/flags"
|
"github.com/ncw/rclone/fs/config/flags"
|
||||||
@ -126,13 +127,14 @@ func init() {
|
|||||||
|
|
||||||
// Fs represents a remote dropbox server
|
// Fs represents a remote dropbox server
|
||||||
type Fs struct {
|
type Fs struct {
|
||||||
name string // name of this remote
|
name string // name of this remote
|
||||||
root string // the path we are working on
|
root string // the path we are working on
|
||||||
features *fs.Features // optional features
|
features *fs.Features // optional features
|
||||||
srv files.Client // the connection to the dropbox server
|
srv files.Client // the connection to the dropbox server
|
||||||
slashRoot string // root with "/" prefix, lowercase
|
sharingClient sharing.Client // as above, but for generating sharing links
|
||||||
slashRootSlash string // root with "/" prefix and postfix, lowercase
|
slashRoot string // root with "/" prefix, lowercase
|
||||||
pacer *pacer.Pacer // To pace the API calls
|
slashRootSlash string // root with "/" prefix and postfix, lowercase
|
||||||
|
pacer *pacer.Pacer // To pace the API calls
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object describes a dropbox object
|
// Object describes a dropbox object
|
||||||
@ -210,11 +212,13 @@ func NewFs(name, root string) (fs.Fs, error) {
|
|||||||
Client: oAuthClient, // maybe???
|
Client: oAuthClient, // maybe???
|
||||||
}
|
}
|
||||||
srv := files.New(config)
|
srv := files.New(config)
|
||||||
|
sharingClient := sharing.New(config)
|
||||||
|
|
||||||
f := &Fs{
|
f := &Fs{
|
||||||
name: name,
|
name: name,
|
||||||
srv: srv,
|
srv: srv,
|
||||||
pacer: pacer.New().SetMinSleep(minSleep).SetMaxSleep(maxSleep).SetDecayConstant(decayConstant),
|
sharingClient: sharingClient,
|
||||||
|
pacer: pacer.New().SetMinSleep(minSleep).SetMaxSleep(maxSleep).SetDecayConstant(decayConstant),
|
||||||
}
|
}
|
||||||
f.features = (&fs.Features{
|
f.features = (&fs.Features{
|
||||||
CaseInsensitive: true,
|
CaseInsensitive: true,
|
||||||
@ -640,6 +644,52 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) {
|
|||||||
return dstObj, nil
|
return dstObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicLink adds a "readable by anyone with link" permission on the given file or folder.
|
||||||
|
func (f *Fs) PublicLink(remote string) (link string, err error) {
|
||||||
|
absPath := "/" + path.Join(f.Root(), remote)
|
||||||
|
fs.Debugf(f, "attempting to share '%s' (absolute path: %s)", remote, absPath)
|
||||||
|
createArg := sharing.CreateSharedLinkWithSettingsArg{
|
||||||
|
Path: absPath,
|
||||||
|
}
|
||||||
|
var linkRes sharing.IsSharedLinkMetadata
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
linkRes, err = f.sharingClient.CreateSharedLinkWithSettings(&createArg)
|
||||||
|
return shouldRetry(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil && strings.Contains(err.Error(), sharing.CreateSharedLinkWithSettingsErrorSharedLinkAlreadyExists) {
|
||||||
|
fs.Debugf(absPath, "has a public link already, attempting to retrieve it")
|
||||||
|
listArg := sharing.ListSharedLinksArg{
|
||||||
|
Path: absPath,
|
||||||
|
DirectOnly: true,
|
||||||
|
}
|
||||||
|
var listRes *sharing.ListSharedLinksResult
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
listRes, err = f.sharingClient.ListSharedLinks(&listArg)
|
||||||
|
return shouldRetry(err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(listRes.Links) == 0 {
|
||||||
|
err = errors.New("Dropbox says the sharing link already exists, but list came back empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
linkRes = listRes.Links[0]
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
switch res := linkRes.(type) {
|
||||||
|
case *sharing.FileLinkMetadata:
|
||||||
|
link = res.Url
|
||||||
|
case *sharing.FolderLinkMetadata:
|
||||||
|
link = res.Url
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Don't know how to extract link, response has unknown format: %T", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// DirMove moves src, srcRemote to this remote at dstRemote
|
// DirMove moves src, srcRemote to this remote at dstRemote
|
||||||
// using server side move operations.
|
// using server side move operations.
|
||||||
//
|
//
|
||||||
@ -975,11 +1025,12 @@ func (o *Object) Remove() (err error) {
|
|||||||
|
|
||||||
// Check the interfaces are satisfied
|
// Check the interfaces are satisfied
|
||||||
var (
|
var (
|
||||||
_ fs.Fs = (*Fs)(nil)
|
_ fs.Fs = (*Fs)(nil)
|
||||||
_ fs.Copier = (*Fs)(nil)
|
_ fs.Copier = (*Fs)(nil)
|
||||||
_ fs.Purger = (*Fs)(nil)
|
_ fs.Purger = (*Fs)(nil)
|
||||||
_ fs.PutStreamer = (*Fs)(nil)
|
_ fs.PutStreamer = (*Fs)(nil)
|
||||||
_ fs.Mover = (*Fs)(nil)
|
_ fs.Mover = (*Fs)(nil)
|
||||||
_ fs.DirMover = (*Fs)(nil)
|
_ fs.PublicLinker = (*Fs)(nil)
|
||||||
_ fs.Object = (*Object)(nil)
|
_ fs.DirMover = (*Fs)(nil)
|
||||||
|
_ fs.Object = (*Object)(nil)
|
||||||
)
|
)
|
||||||
|
@ -71,6 +71,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -71,6 +71,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -71,6 +71,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -68,6 +68,7 @@ func TestObjectUpdate(t *testing.T) { fstests.TestObjectUpdate(t) }
|
|||||||
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
func TestObjectStorable(t *testing.T) { fstests.TestObjectStorable(t) }
|
||||||
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
func TestFsIsFile(t *testing.T) { fstests.TestFsIsFile(t) }
|
||||||
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
func TestFsIsFileNotFound(t *testing.T) { fstests.TestFsIsFileNotFound(t) }
|
||||||
|
func TestPublicLink(t *testing.T) { fstests.TestPublicLink(t) }
|
||||||
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
func TestObjectRemove(t *testing.T) { fstests.TestObjectRemove(t) }
|
||||||
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
func TestFsPutStream(t *testing.T) { fstests.TestFsPutStream(t) }
|
||||||
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
func TestObjectPurge(t *testing.T) { fstests.TestObjectPurge(t) }
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
_ "github.com/ncw/rclone/cmd/genautocomplete"
|
_ "github.com/ncw/rclone/cmd/genautocomplete"
|
||||||
_ "github.com/ncw/rclone/cmd/gendocs"
|
_ "github.com/ncw/rclone/cmd/gendocs"
|
||||||
_ "github.com/ncw/rclone/cmd/info"
|
_ "github.com/ncw/rclone/cmd/info"
|
||||||
|
_ "github.com/ncw/rclone/cmd/link"
|
||||||
_ "github.com/ncw/rclone/cmd/listremotes"
|
_ "github.com/ncw/rclone/cmd/listremotes"
|
||||||
_ "github.com/ncw/rclone/cmd/ls"
|
_ "github.com/ncw/rclone/cmd/ls"
|
||||||
_ "github.com/ncw/rclone/cmd/lsd"
|
_ "github.com/ncw/rclone/cmd/lsd"
|
||||||
|
@ -141,10 +141,10 @@ func ShowVersion() {
|
|||||||
fmt.Printf("- go version: %s\n", runtime.Version())
|
fmt.Printf("- go version: %s\n", runtime.Version())
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFsFile creates a dst Fs from a name but may point to a file.
|
// NewFsFile creates a dst Fs from a name but may point to a file.
|
||||||
//
|
//
|
||||||
// It returns a string with the file name if points to a file
|
// It returns a string with the file name if points to a file
|
||||||
func newFsFile(remote string) (fs.Fs, string) {
|
func NewFsFile(remote string) (fs.Fs, string) {
|
||||||
fsInfo, configName, fsPath, err := fs.ParseRemote(remote)
|
fsInfo, configName, fsPath, err := fs.ParseRemote(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.CountError(err)
|
fs.CountError(err)
|
||||||
@ -169,7 +169,7 @@ func newFsFile(remote string) (fs.Fs, string) {
|
|||||||
//
|
//
|
||||||
// This can point to a file
|
// This can point to a file
|
||||||
func newFsSrc(remote string) (fs.Fs, string) {
|
func newFsSrc(remote string) (fs.Fs, string) {
|
||||||
f, fileName := newFsFile(remote)
|
f, fileName := NewFsFile(remote)
|
||||||
if fileName != "" {
|
if fileName != "" {
|
||||||
if !filter.Active.InActive() {
|
if !filter.Active.InActive() {
|
||||||
err := errors.Errorf("Can't limit to single files when using filters: %v", remote)
|
err := errors.Errorf("Can't limit to single files when using filters: %v", remote)
|
||||||
|
41
cmd/link/link.go
Normal file
41
cmd/link/link.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package link
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ncw/rclone/cmd"
|
||||||
|
"github.com/ncw/rclone/fs/operations"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmd.Root.AddCommand(commandDefintion)
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandDefintion = &cobra.Command{
|
||||||
|
Use: "link remote:path",
|
||||||
|
Short: `Generate public link to file/folder.`,
|
||||||
|
Long: `
|
||||||
|
rclone link will create or retrieve a public link to the given file or folder.
|
||||||
|
|
||||||
|
rclone link remote:path/to/file
|
||||||
|
rclone link remote:path/to/folder/
|
||||||
|
|
||||||
|
If successful, the last line of the output will contain the link. Exact
|
||||||
|
capabilities depend on the remote, but the link will always be created with
|
||||||
|
the least constraints – e.g. no expiry, no password protection, accessible
|
||||||
|
without account.
|
||||||
|
`,
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
cmd.CheckArgs(1, 1, command, args)
|
||||||
|
fsrc, remote := cmd.NewFsFile(args[0])
|
||||||
|
cmd.Run(false, false, command, func() error {
|
||||||
|
link, err := operations.PublicLink(fsrc, remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(link)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
@ -119,27 +119,27 @@ All the remotes support a basic set of features, but there are some
|
|||||||
optional features supported by some remotes used to make some
|
optional features supported by some remotes used to make some
|
||||||
operations more efficient.
|
operations more efficient.
|
||||||
|
|
||||||
| Name | Purge | Copy | Move | DirMove | CleanUp | ListR | StreamUpload |
|
| Name | Purge | Copy | Move | DirMove | CleanUp | ListR | StreamUpload | LinkSharing |
|
||||||
| ---------------------------- |:-----:|:----:|:----:|:-------:|:-------:|:-----:|:------------:|
|
| ---------------------------- |:-----:|:----:|:----:|:-------:|:-------:|:-----:|:------------:|:------------:|
|
||||||
| Amazon Drive | Yes | No | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | No |
|
| Amazon Drive | Yes | No | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Amazon S3 | No | Yes | No | No | No | Yes | Yes |
|
| Amazon S3 | No | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Backblaze B2 | No | No | No | No | Yes | Yes | Yes |
|
| Backblaze B2 | No | No | No | No | Yes | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Box | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes |
|
| Box | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Dropbox | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes |
|
| Dropbox | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes | Yes |
|
||||||
| FTP | No | No | Yes | Yes | No | No | Yes |
|
| FTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Google Cloud Storage | Yes | Yes | No | No | No | Yes | Yes |
|
| Google Cloud Storage | Yes | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Google Drive | Yes | Yes | Yes | Yes | Yes | No | Yes |
|
| Google Drive | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes |
|
||||||
| HTTP | No | No | No | No | No | No | No |
|
| HTTP | No | No | No | No | No | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Hubic | Yes † | Yes | No | No | No | Yes | Yes |
|
| Hubic | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Microsoft Azure Blob Storage | Yes | Yes | No | No | No | Yes | No |
|
| Microsoft Azure Blob Storage | Yes | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Microsoft OneDrive | Yes | Yes | Yes | No [#197](https://github.com/ncw/rclone/issues/197) | No [#575](https://github.com/ncw/rclone/issues/575) | No | No |
|
| Microsoft OneDrive | Yes | Yes | Yes | No [#197](https://github.com/ncw/rclone/issues/197) | No [#575](https://github.com/ncw/rclone/issues/575) | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Openstack Swift | Yes † | Yes | No | No | No | Yes | Yes |
|
| Openstack Swift | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| pCloud | Yes | Yes | Yes | Yes | Yes | No | No |
|
| pCloud | Yes | Yes | Yes | Yes | Yes | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| QingStor | No | Yes | No | No | No | Yes | No |
|
| QingStor | No | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| SFTP | No | No | Yes | Yes | No | No | Yes |
|
| SFTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| WebDAV | Yes | Yes | Yes | Yes | No | No | Yes ‡ |
|
| WebDAV | Yes | Yes | Yes | Yes | No | No | Yes ‡ | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| Yandex Disk | Yes | No | No | No | Yes | Yes | Yes |
|
| Yandex Disk | Yes | No | No | No | Yes | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) |
|
||||||
| The local filesystem | Yes | No | Yes | Yes | No | No | Yes |
|
| The local filesystem | Yes | No | Yes | Yes | No | No | Yes | No |
|
||||||
|
|
||||||
### Purge ###
|
### Purge ###
|
||||||
|
|
||||||
@ -196,3 +196,9 @@ See the [rclone docs](/docs/#fast-list) for more details.
|
|||||||
Some remotes allow files to be uploaded without knowing the file size
|
Some remotes allow files to be uploaded without knowing the file size
|
||||||
in advance. This allows certain operations to work without spooling the
|
in advance. This allows certain operations to work without spooling the
|
||||||
file to local disk first, e.g. `rclone rcat`.
|
file to local disk first, e.g. `rclone rcat`.
|
||||||
|
|
||||||
|
### LinkSharing ###
|
||||||
|
|
||||||
|
Sets the necessary permissions on a file or folder and prints a link
|
||||||
|
that allows others to access them, even if they don't have an account
|
||||||
|
on the particular cloud provider.
|
||||||
|
12
fs/fs.go
12
fs/fs.go
@ -328,6 +328,9 @@ type Features struct {
|
|||||||
// as an optional interface
|
// as an optional interface
|
||||||
DirCacheFlush func()
|
DirCacheFlush func()
|
||||||
|
|
||||||
|
// PublicLink generates a public link to the remote path (usually readable by anyone)
|
||||||
|
PublicLink func(remote string) (string, error)
|
||||||
|
|
||||||
// Put in to the remote path with the modTime given of the given size
|
// Put in to the remote path with the modTime given of the given size
|
||||||
//
|
//
|
||||||
// May create the object even if it returns an error - if so
|
// May create the object even if it returns an error - if so
|
||||||
@ -443,6 +446,9 @@ func (ft *Features) Fill(f Fs) *Features {
|
|||||||
if do, ok := f.(DirCacheFlusher); ok {
|
if do, ok := f.(DirCacheFlusher); ok {
|
||||||
ft.DirCacheFlush = do.DirCacheFlush
|
ft.DirCacheFlush = do.DirCacheFlush
|
||||||
}
|
}
|
||||||
|
if do, ok := f.(PublicLinker); ok {
|
||||||
|
ft.PublicLink = do.PublicLink
|
||||||
|
}
|
||||||
if do, ok := f.(PutUncheckeder); ok {
|
if do, ok := f.(PutUncheckeder); ok {
|
||||||
ft.PutUnchecked = do.PutUnchecked
|
ft.PutUnchecked = do.PutUnchecked
|
||||||
}
|
}
|
||||||
@ -642,6 +648,12 @@ type PutStreamer interface {
|
|||||||
PutStream(in io.Reader, src ObjectInfo, options ...OpenOption) (Object, error)
|
PutStream(in io.Reader, src ObjectInfo, options ...OpenOption) (Object, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicLinker is an optional interface for Fs
|
||||||
|
type PublicLinker interface {
|
||||||
|
// PublicLink generates a public link to the remote path (usually readable by anyone)
|
||||||
|
PublicLink(remote string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
// MergeDirser is an option interface for Fs
|
// MergeDirser is an option interface for Fs
|
||||||
type MergeDirser interface {
|
type MergeDirser interface {
|
||||||
// MergeDirs merges the contents of all the directories passed
|
// MergeDirs merges the contents of all the directories passed
|
||||||
|
@ -1470,6 +1470,15 @@ func Rcat(fdst fs.Fs, dstFileName string, in io.ReadCloser, modTime time.Time) (
|
|||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicLink adds a "readable by anyone with link" permission on the given file or folder.
|
||||||
|
func PublicLink(f fs.Fs, remote string) (string, error) {
|
||||||
|
doPublicLink := f.Features().PublicLink
|
||||||
|
if doPublicLink == nil {
|
||||||
|
return "", errors.Errorf("%v doesn't support public links", f)
|
||||||
|
}
|
||||||
|
return doPublicLink(remote)
|
||||||
|
}
|
||||||
|
|
||||||
// Rmdirs removes any empty directories (or directories only
|
// Rmdirs removes any empty directories (or directories only
|
||||||
// containing empty directories) under f, including f.
|
// containing empty directories) under f, including f.
|
||||||
func Rmdirs(f fs.Fs, dir string, leaveRoot bool) error {
|
func Rmdirs(f fs.Fs, dir string, leaveRoot bool) error {
|
||||||
|
@ -948,6 +948,62 @@ func TestFsIsFileNotFound(t *testing.T) {
|
|||||||
fstest.CheckListing(t, fileRemote, []fstest.Item{})
|
fstest.CheckListing(t, fileRemote, []fstest.Item{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPublicLink tests creation of sharable, public links
|
||||||
|
func TestPublicLink(t *testing.T) {
|
||||||
|
skipIfNotOk(t)
|
||||||
|
|
||||||
|
doPublicLink := remote.Features().PublicLink
|
||||||
|
if doPublicLink == nil {
|
||||||
|
t.Skip("FS has no PublicLinker interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if object not found
|
||||||
|
link, err := doPublicLink(file1.Path + "_does_not_exist")
|
||||||
|
require.Error(t, err, "Expected to get error when file doesn't exist")
|
||||||
|
require.Equal(t, "", link, "Expected link to be empty on error")
|
||||||
|
|
||||||
|
// sharing file for the first time
|
||||||
|
link1, err := doPublicLink(file1.Path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, "", link1, "Link should not be empty")
|
||||||
|
|
||||||
|
link2, err := doPublicLink(file2.Path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, "", link2, "Link should not be empty")
|
||||||
|
|
||||||
|
require.NotEqual(t, link1, link2, "Links to different files should differ")
|
||||||
|
|
||||||
|
// sharing file for the 2nd time
|
||||||
|
link1, err = doPublicLink(file1.Path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, "", link1, "Link should not be empty")
|
||||||
|
|
||||||
|
// sharing directory for the first time
|
||||||
|
path := path.Dir(file2.Path)
|
||||||
|
link3, err := doPublicLink(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, "", link3, "Link should not be empty")
|
||||||
|
|
||||||
|
// sharing directory for the second time
|
||||||
|
link3, err = doPublicLink(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, "", link3, "Link should not be empty")
|
||||||
|
|
||||||
|
// sharing the "root" directory in a subremote
|
||||||
|
subRemote, _, removeSubRemote, err := fstest.RandomRemote(RemoteName, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer removeSubRemote()
|
||||||
|
// ensure sub remote isn't empty
|
||||||
|
buf := bytes.NewBufferString("somecontent")
|
||||||
|
obji := object.NewStaticObjectInfo("somefile", time.Now(), int64(buf.Len()), true, nil, nil)
|
||||||
|
_, err = subRemote.Put(buf, obji)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
link4, err := subRemote.Features().PublicLink("")
|
||||||
|
require.NoError(t, err, "Sharing root in a sub-remote should work")
|
||||||
|
require.NotEqual(t, "", link4, "Link should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
// TestObjectRemove tests Remove
|
// TestObjectRemove tests Remove
|
||||||
func TestObjectRemove(t *testing.T) {
|
func TestObjectRemove(t *testing.T) {
|
||||||
skipIfNotOk(t)
|
skipIfNotOk(t)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user