1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // cookieauth uses a “Netscape cookie file” to implement the GOAUTH protocol
6 // described in https://golang.org/issue/26232.
7 // It expects the location of the file as the first command-line argument.
9 // Example GOAUTH usage:
10 // export GOAUTH="cookieauth $(git config --get http.cookieFile)"
12 // See http://www.cookiecentral.com/faq/#3.5 for a description of the Netscape
13 // cookie file format.
33 fmt.Fprintf(os.Stderr, "usage: %s COOKIEFILE [URL]\n", os.Args[0])
37 log.SetPrefix("cookieauth: ")
39 f, err := os.Open(os.Args[1])
41 log.Fatalf("failed to read cookie file: %v\n", os.Args[1])
48 targetURLs = map[string]*url.URL{}
50 if len(os.Args) == 3 {
51 targetURL, err = url.ParseRequestURI(os.Args[2])
53 log.Fatalf("invalid request URI (%v): %q\n", err, os.Args[2])
55 targetURLs[targetURL.String()] = targetURL
56 } else if len(os.Args) > 3 {
57 // Extra arguments were passed: maybe the protocol was expanded?
58 // We don't know how to interpret the request, so ignore it.
62 entries, err := parseCookieFile(f.Name(), f)
64 log.Fatalf("error reading cookie file: %v\n", f.Name())
67 jar, err := cookiejar.New(nil)
69 log.Fatalf("failed to initialize cookie jar: %v\n", err)
72 for _, e := range entries {
80 targetURLs[u.String()] = u
83 jar.SetCookies(u, []*http.Cookie{&e.Cookie})
86 for _, u := range targetURLs {
87 req := &http.Request{URL: u, Header: make(http.Header)}
88 for _, c := range jar.Cookies(req.URL) {
91 fmt.Printf("%s\n\n", u)
92 req.Header.Write(os.Stdout)
102 // parseCookieFile parses a Netscape cookie file as described in
103 // http://www.cookiecentral.com/faq/#3.5.
104 func parseCookieFile(name string, r io.Reader) ([]*Entry, error) {
106 s := bufio.NewScanner(r)
110 text := strings.TrimSpace(s.Text())
111 if len(text) < 2 || (text[0] == '#' && unicode.IsSpace(rune(text[1]))) {
115 e, err := parseCookieLine(text)
117 log.Printf("%s:%d: %v\n", name, line, err)
120 entries = append(entries, e)
122 return entries, s.Err()
125 func parseCookieLine(line string) (*Entry, error) {
126 f := strings.Fields(line)
128 return nil, fmt.Errorf("found %d columns; want 7", len(f))
134 if domain := f[0]; strings.HasPrefix(domain, "#HttpOnly_") {
136 e.Host = strings.TrimPrefix(domain[10:], ".")
138 e.Host = strings.TrimPrefix(domain, ".")
141 isDomain, err := strconv.ParseBool(f[1])
143 return nil, fmt.Errorf("non-boolean domain flag: %v", err)
151 c.Secure, err = strconv.ParseBool(f[3])
153 return nil, fmt.Errorf("non-boolean secure flag: %v", err)
156 expiration, err := strconv.ParseInt(f[4], 10, 64)
158 return nil, fmt.Errorf("malformed expiration: %v", err)
160 c.Expires = time.Unix(expiration, 0)