From 738520bbdbe9ced7ce8fc4bb493353d6e289ce92e723c2ef6667dbf59dc7adab Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Sat, 12 Aug 2023 12:17:32 +0300 Subject: [PATCH 1/1] Initial commit --- go.mod | 3 ++ netrc.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ netrc_test.go | 53 +++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 go.mod create mode 100644 netrc.go create mode 100644 netrc_test.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b191185 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module go.cypherpunks.ru/netrc + +go 1.20 diff --git a/netrc.go b/netrc.go new file mode 100644 index 0000000..53521d2 --- /dev/null +++ b/netrc.go @@ -0,0 +1,92 @@ +// netrc -- parse .netrc file +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// netrc library parses .netrc file. It is copy-paste of +// golang.org/x/tools/cmd/auth/netrcauth. +// +// See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html +// for documentation on the .netrc format. +package netrc + +import ( + "os" + "path" + "strings" +) + +type Line struct { + Machine string + Login string + Password string +} + +func Parse(data string) []Line { + var nrc []Line + var l Line + inMacro := false + for _, line := range strings.Split(data, "\n") { + if inMacro { + if line == "" { + inMacro = false + } + continue + } + f := strings.Fields(line) + i := 0 + for ; i < len(f)-1; i += 2 { + // Reset at each "machine" token. + // "The auto-login process searches the .netrc file for a machine token + // that matches […]. Once a match is made, the subsequent .netrc tokens + // are processed, stopping when the end of file is reached or another + // machine or a default token is encountered." + switch f[i] { + case "machine": + l = Line{Machine: f[i+1]} + case "default": + break + case "login": + l.Login = f[i+1] + case "password": + l.Password = f[i+1] + case "macdef": + // "A macro is defined with the specified name; its contents begin with + // the next .netrc line and continue until a null line (consecutive + // new-line characters) is encountered." + inMacro = true + } + if l.Machine != "" && l.Login != "" && l.Password != "" { + nrc = append(nrc, l) + l = Line{} + } + } + if i < len(f) && f[i] == "default" { + // "There can be only one default token, and it must be after all machine tokens." + break + } + } + return nrc +} + +func Path() (netrc string) { + p, ok := os.LookupEnv("NETRC") + if ok { + return p + } + homeDir, _ := os.UserHomeDir() + return path.Join(homeDir, ".netrc") +} + +func Find(host string) (login string, password string) { + data, err := os.ReadFile(Path()) + if err != nil { + return "", "" + } + for _, l := range Parse(string(data)) { + if l.Machine == host { + return l.Login, l.Password + } + } + return "", "" +} diff --git a/netrc_test.go b/netrc_test.go new file mode 100644 index 0000000..7fe98c2 --- /dev/null +++ b/netrc_test.go @@ -0,0 +1,53 @@ +package netrc + +import ( + "reflect" + "testing" +) + +var testNetrc = ` +machine incomplete +password none + +machine api.github.com + login user + password pwd + +machine incomlete.host + login justlogin + +machine test.host +login user2 +password pwd2 + +machine oneline login user3 password pwd3 + +machine ignore.host macdef ignore + login nobody + password nothing + +machine hasmacro.too macdef ignore-next-lines login user4 password pwd4 + login nobody + password nothing + +default +login anonymous +password gopher@golang.org + +machine after.default +login oops +password too-late-in-file +` + +func TestParse(t *testing.T) { + lines := Parse(testNetrc) + want := []Line{ + {"api.github.com", "user", "pwd"}, + {"test.host", "user2", "pwd2"}, + {"oneline", "user3", "pwd3"}, + {"hasmacro.too", "user4", "pwd4"}, + } + if !reflect.DeepEqual(lines, want) { + t.Errorf("have %q\nwant %q", lines, want) + } +} -- 2.44.0