From 3ebcb555f4457fc1cfa1fc3965d1e74ed125ce97 Mon Sep 17 00:00:00 2001
From: Nick Craig-Wood <nick@craig-wood.com>
Date: Mon, 12 Aug 2019 11:07:31 +0100
Subject: [PATCH] fs: add optional features UserInfo and Disconnect

---
 backend/cache/cache.go | 20 ++++++++++++++++++++
 backend/crypt/crypt.go | 20 ++++++++++++++++++++
 fs/fs.go               | 31 +++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+)

diff --git a/backend/cache/cache.go b/backend/cache/cache.go
index 469cc9f73..000c4bc2f 100644
--- a/backend/cache/cache.go
+++ b/backend/cache/cache.go
@@ -1864,6 +1864,24 @@ func cleanPath(p string) string {
 	return p
 }
 
+// UserInfo returns info about the connected user
+func (f *Fs) UserInfo(ctx context.Context) (map[string]string, error) {
+	do := f.Fs.Features().UserInfo
+	if do == nil {
+		return nil, fs.ErrorNotImplemented
+	}
+	return do(ctx)
+}
+
+// Disconnect the current user
+func (f *Fs) Disconnect(ctx context.Context) error {
+	do := f.Fs.Features().Disconnect
+	if do == nil {
+		return fs.ErrorNotImplemented
+	}
+	return do(ctx)
+}
+
 // Check the interfaces are satisfied
 var (
 	_ fs.Fs             = (*Fs)(nil)
@@ -1879,4 +1897,6 @@ var (
 	_ fs.ListRer        = (*Fs)(nil)
 	_ fs.ChangeNotifier = (*Fs)(nil)
 	_ fs.Abouter        = (*Fs)(nil)
+	_ fs.UserInfoer     = (*Fs)(nil)
+	_ fs.Disconnecter   = (*Fs)(nil)
 )
diff --git a/backend/crypt/crypt.go b/backend/crypt/crypt.go
index c03392a1d..ae92e22ed 100644
--- a/backend/crypt/crypt.go
+++ b/backend/crypt/crypt.go
@@ -802,6 +802,24 @@ func (f *Fs) newDir(ctx context.Context, dir fs.Directory) fs.Directory {
 	return newDir
 }
 
+// UserInfo returns info about the connected user
+func (f *Fs) UserInfo(ctx context.Context) (map[string]string, error) {
+	do := f.Fs.Features().UserInfo
+	if do == nil {
+		return nil, fs.ErrorNotImplemented
+	}
+	return do(ctx)
+}
+
+// Disconnect the current user
+func (f *Fs) Disconnect(ctx context.Context) error {
+	do := f.Fs.Features().Disconnect
+	if do == nil {
+		return fs.ErrorNotImplemented
+	}
+	return do(ctx)
+}
+
 // ObjectInfo describes a wrapped fs.ObjectInfo for being the source
 //
 // This encrypts the remote name and adjusts the size
@@ -888,6 +906,8 @@ var (
 	_ fs.DirCacheFlusher = (*Fs)(nil)
 	_ fs.ChangeNotifier  = (*Fs)(nil)
 	_ fs.PublicLinker    = (*Fs)(nil)
+	_ fs.UserInfoer      = (*Fs)(nil)
+	_ fs.Disconnecter    = (*Fs)(nil)
 	_ fs.ObjectInfo      = (*ObjectInfo)(nil)
 	_ fs.Object          = (*Object)(nil)
 	_ fs.ObjectUnWrapper = (*Object)(nil)
diff --git a/fs/fs.go b/fs/fs.go
index c6febd29f..f2f0d3f86 100644
--- a/fs/fs.go
+++ b/fs/fs.go
@@ -69,6 +69,7 @@ var (
 	ErrorImmutableModified           = errors.New("immutable file modified")
 	ErrorPermissionDenied            = errors.New("permission denied")
 	ErrorCantShareDirectories        = errors.New("this backend can't share directories with link")
+	ErrorNotImplemented              = errors.New("optional feature not implemented")
 )
 
 // RegInfo provides information about a filesystem
@@ -588,6 +589,12 @@ type Features struct {
 	//
 	// It truncates any existing object
 	OpenWriterAt func(ctx context.Context, remote string, size int64) (WriterAtCloser, error)
+
+	// UserInfo returns info about the connected user
+	UserInfo func(ctx context.Context) (map[string]string, error)
+
+	// Disconnect the current user
+	Disconnect func(ctx context.Context) error
 }
 
 // Disable nil's out the named feature.  If it isn't found then it
@@ -703,6 +710,12 @@ func (ft *Features) Fill(f Fs) *Features {
 	if do, ok := f.(OpenWriterAter); ok {
 		ft.OpenWriterAt = do.OpenWriterAt
 	}
+	if do, ok := f.(UserInfoer); ok {
+		ft.UserInfo = do.UserInfo
+	}
+	if do, ok := f.(Disconnecter); ok {
+		ft.Disconnect = do.Disconnect
+	}
 	return ft.DisableList(Config.DisableFeatures)
 }
 
@@ -771,6 +784,12 @@ func (ft *Features) Mask(f Fs) *Features {
 	if mask.OpenWriterAt == nil {
 		ft.OpenWriterAt = nil
 	}
+	if mask.UserInfo == nil {
+		ft.UserInfo = nil
+	}
+	if mask.Disconnect == nil {
+		ft.Disconnect = nil
+	}
 	return ft.DisableList(Config.DisableFeatures)
 }
 
@@ -980,6 +999,18 @@ type OpenWriterAter interface {
 	OpenWriterAt(ctx context.Context, remote string, size int64) (WriterAtCloser, error)
 }
 
+// UserInfoer is an optional interface for Fs
+type UserInfoer interface {
+	// UserInfo returns info about the connected user
+	UserInfo(ctx context.Context) (map[string]string, error)
+}
+
+// Disconnecter is an optional interface for Fs
+type Disconnecter interface {
+	// Disconnect the current user
+	Disconnect(ctx context.Context) error
+}
+
 // ObjectsChan is a channel of Objects
 type ObjectsChan chan Object