github.com/mckael/restic@v0.8.3/internal/backend/sftp/config.go (about)

     1  package sftp
     2  
     3  import (
     4  	"net/url"
     5  	"path"
     6  	"strings"
     7  
     8  	"github.com/restic/restic/internal/errors"
     9  	"github.com/restic/restic/internal/options"
    10  )
    11  
    12  // Config collects all information required to connect to an sftp server.
    13  type Config struct {
    14  	User, Host, Path string
    15  	Layout           string `option:"layout" help:"use this backend directory layout (default: auto-detect)"`
    16  	Command          string `option:"command" help:"specify command to create sftp connection"`
    17  }
    18  
    19  func init() {
    20  	options.Register("sftp", Config{})
    21  }
    22  
    23  // ParseConfig parses the string s and extracts the sftp config. The
    24  // supported configuration formats are sftp://user@host/directory
    25  //  and sftp:user@host:directory.  The directory will be path Cleaned and can
    26  //  be an absolute path if it starts with a '/' (e.g.
    27  //  sftp://user@host//absolute and sftp:user@host:/absolute).
    28  func ParseConfig(s string) (interface{}, error) {
    29  	var user, host, dir string
    30  	switch {
    31  	case strings.HasPrefix(s, "sftp://"):
    32  		// parse the "sftp://user@host/path" url format
    33  		url, err := url.Parse(s)
    34  		if err != nil {
    35  			return nil, errors.Wrap(err, "url.Parse")
    36  		}
    37  		if url.User != nil {
    38  			user = url.User.Username()
    39  		}
    40  		host = url.Host
    41  		dir = url.Path
    42  		if dir == "" {
    43  			return nil, errors.Errorf("invalid backend %q, no directory specified", s)
    44  		}
    45  
    46  		dir = dir[1:]
    47  	case strings.HasPrefix(s, "sftp:"):
    48  		// parse the sftp:user@host:path format, which means we'll get
    49  		// "user@host:path" in s
    50  		s = s[5:]
    51  		// split user@host and path at the colon
    52  		data := strings.SplitN(s, ":", 2)
    53  		if len(data) < 2 {
    54  			return nil, errors.New("sftp: invalid format, hostname or path not found")
    55  		}
    56  		host = data[0]
    57  		dir = data[1]
    58  		// split user and host at the "@"
    59  		data = strings.SplitN(host, "@", 2)
    60  		if len(data) == 2 {
    61  			user = data[0]
    62  			host = data[1]
    63  		}
    64  	default:
    65  		return nil, errors.New(`invalid format, does not start with "sftp:"`)
    66  	}
    67  
    68  	p := path.Clean(dir)
    69  	if strings.HasPrefix(p, "~") {
    70  		return nil, errors.Fatal("sftp path starts with the tilde (~) character, that fails for most sftp servers.\nUse a relative directory, most servers interpret this as relative to the user's home directory.")
    71  	}
    72  
    73  	return Config{
    74  		User: user,
    75  		Host: host,
    76  		Path: p,
    77  	}, nil
    78  }