// 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. // netrcauth uses a .netrc file (or _netrc file on Windows) to implement the // GOAUTH protocol described in https://golang.org/issue/26232. // It expects the location of the file as the first command-line argument. // // Example GOAUTH usage: // export GOAUTH="netrcauth $HOME/.netrc" // // See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html // or run 'man 5 netrc' for a description of the .netrc file format. package main import ( "fmt" "io/ioutil" "log" "net/http" "net/url" "os" "strings" ) func main() { if len(os.Args) < 2 { fmt.Fprintf(os.Stderr, "usage: %s NETRCFILE [URL]", os.Args[0]) os.Exit(2) } log.SetPrefix("netrcauth: ") if len(os.Args) != 2 { // An explicit URL was passed on the command line, but netrcauth does not // have any URL-specific output: it dumps the entire .netrc file at the // first call. return } path := os.Args[1] data, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) { return } log.Fatalf("failed to read %s: %v\n", path, err) } u := &url.URL{Scheme: "https"} lines := parseNetrc(string(data)) for _, l := range lines { u.Host = l.machine fmt.Printf("%s\n\n", u) req := &http.Request{Header: make(http.Header)} req.SetBasicAuth(l.login, l.password) req.Header.Write(os.Stdout) fmt.Println() } } // The following functions were extracted from src/cmd/go/internal/web2/web.go // as of https://golang.org/cl/161698. type netrcLine struct { machine string login string password string } func parseNetrc(data string) []netrcLine { // See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html // for documentation on the .netrc format. var nrc []netrcLine var l netrcLine 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 = netrcLine{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 = netrcLine{} } } 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 }