...

Source file src/google.golang.org/api/examples/main.go

Documentation: google.golang.org/api/examples

     1  // Copyright 2011 Google LLC. 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.
     4  
     5  package main
     6  
     7  import (
     8  	"context"
     9  	"encoding/gob"
    10  	"errors"
    11  	"flag"
    12  	"fmt"
    13  	"hash/fnv"
    14  	"log"
    15  	"net/http"
    16  	"net/http/httptest"
    17  	"net/url"
    18  	"os"
    19  	"os/exec"
    20  	"path/filepath"
    21  	"runtime"
    22  	"strings"
    23  	"time"
    24  
    25  	"golang.org/x/oauth2"
    26  	"golang.org/x/oauth2/google"
    27  )
    28  
    29  // Flags
    30  var (
    31  	clientID     = flag.String("clientid", "", "OAuth 2.0 Client ID.  If non-empty, overrides --clientid_file")
    32  	clientIDFile = flag.String("clientid-file", "clientid.dat",
    33  		"Name of a file containing just the project's OAuth 2.0 Client ID from https://developers.google.com/console.")
    34  	secret     = flag.String("secret", "", "OAuth 2.0 Client Secret.  If non-empty, overrides --secret_file")
    35  	secretFile = flag.String("secret-file", "clientsecret.dat",
    36  		"Name of a file containing just the project's OAuth 2.0 Client Secret from https://developers.google.com/console.")
    37  	cacheToken = flag.Bool("cachetoken", true, "cache the OAuth 2.0 token")
    38  	debug      = flag.Bool("debug", false, "show HTTP traffic")
    39  )
    40  
    41  func usage() {
    42  	fmt.Fprintf(os.Stderr, "Usage: go-api-demo <api-demo-name> [api name args]\n\nPossible APIs:\n\n")
    43  	for n := range demoFunc {
    44  		fmt.Fprintf(os.Stderr, "  * %s\n", n)
    45  	}
    46  	os.Exit(2)
    47  }
    48  
    49  func main() {
    50  	flag.Parse()
    51  	if flag.NArg() == 0 {
    52  		usage()
    53  	}
    54  
    55  	name := flag.Arg(0)
    56  	demo, ok := demoFunc[name]
    57  	if !ok {
    58  		usage()
    59  	}
    60  
    61  	config := &oauth2.Config{
    62  		ClientID:     valueOrFileContents(*clientID, *clientIDFile),
    63  		ClientSecret: valueOrFileContents(*secret, *secretFile),
    64  		Endpoint:     google.Endpoint,
    65  		Scopes:       []string{demoScope[name]},
    66  	}
    67  
    68  	ctx := context.Background()
    69  	if *debug {
    70  		ctx = context.WithValue(ctx, oauth2.HTTPClient, &http.Client{
    71  			Transport: &logTransport{http.DefaultTransport},
    72  		})
    73  	}
    74  	c := newOAuthClient(ctx, config)
    75  	demo(c, flag.Args()[1:])
    76  }
    77  
    78  var (
    79  	demoFunc  = make(map[string]func(*http.Client, []string))
    80  	demoScope = make(map[string]string)
    81  )
    82  
    83  func registerDemo(name, scope string, main func(c *http.Client, argv []string)) {
    84  	if demoFunc[name] != nil {
    85  		panic(name + " already registered")
    86  	}
    87  	demoFunc[name] = main
    88  	demoScope[name] = scope
    89  }
    90  
    91  func osUserCacheDir() string {
    92  	switch runtime.GOOS {
    93  	case "darwin":
    94  		return filepath.Join(os.Getenv("HOME"), "Library", "Caches")
    95  	case "linux", "freebsd":
    96  		return filepath.Join(os.Getenv("HOME"), ".cache")
    97  	}
    98  	log.Printf("TODO: osUserCacheDir on GOOS %q", runtime.GOOS)
    99  	return "."
   100  }
   101  
   102  func tokenCacheFile(config *oauth2.Config) string {
   103  	hash := fnv.New32a()
   104  	hash.Write([]byte(config.ClientID))
   105  	hash.Write([]byte(config.ClientSecret))
   106  	hash.Write([]byte(strings.Join(config.Scopes, " ")))
   107  	fn := fmt.Sprintf("go-api-demo-tok%v", hash.Sum32())
   108  	return filepath.Join(osUserCacheDir(), url.QueryEscape(fn))
   109  }
   110  
   111  func tokenFromFile(file string) (*oauth2.Token, error) {
   112  	if !*cacheToken {
   113  		return nil, errors.New("--cachetoken is false")
   114  	}
   115  	f, err := os.Open(file)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	t := new(oauth2.Token)
   120  	err = gob.NewDecoder(f).Decode(t)
   121  	return t, err
   122  }
   123  
   124  func saveToken(file string, token *oauth2.Token) {
   125  	f, err := os.Create(file)
   126  	if err != nil {
   127  		log.Printf("Warning: failed to cache oauth token: %v", err)
   128  		return
   129  	}
   130  	defer f.Close()
   131  	gob.NewEncoder(f).Encode(token)
   132  }
   133  
   134  func newOAuthClient(ctx context.Context, config *oauth2.Config) *http.Client {
   135  	cacheFile := tokenCacheFile(config)
   136  	token, err := tokenFromFile(cacheFile)
   137  	if err != nil {
   138  		token = tokenFromWeb(ctx, config)
   139  		saveToken(cacheFile, token)
   140  	} else {
   141  		log.Printf("Using cached token %#v from %q", token, cacheFile)
   142  	}
   143  
   144  	return config.Client(ctx, token)
   145  }
   146  
   147  func tokenFromWeb(ctx context.Context, config *oauth2.Config) *oauth2.Token {
   148  	ch := make(chan string)
   149  	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
   150  	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   151  		if req.URL.Path == "/favicon.ico" {
   152  			http.Error(rw, "", 404)
   153  			return
   154  		}
   155  		if req.FormValue("state") != randState {
   156  			log.Printf("State doesn't match: req = %#v", req)
   157  			http.Error(rw, "", 500)
   158  			return
   159  		}
   160  		if code := req.FormValue("code"); code != "" {
   161  			fmt.Fprintf(rw, "<h1>Success</h1>Authorized.")
   162  			rw.(http.Flusher).Flush()
   163  			ch <- code
   164  			return
   165  		}
   166  		log.Printf("no code")
   167  		http.Error(rw, "", 500)
   168  	}))
   169  	defer ts.Close()
   170  
   171  	config.RedirectURL = ts.URL
   172  	authURL := config.AuthCodeURL(randState)
   173  	go openURL(authURL)
   174  	log.Printf("Authorize this app at: %s", authURL)
   175  	code := <-ch
   176  	log.Printf("Got code: %s", code)
   177  
   178  	token, err := config.Exchange(ctx, code)
   179  	if err != nil {
   180  		log.Fatalf("Token exchange error: %v", err)
   181  	}
   182  	return token
   183  }
   184  
   185  func openURL(url string) {
   186  	try := []string{"xdg-open", "google-chrome", "open"}
   187  	for _, bin := range try {
   188  		err := exec.Command(bin, url).Run()
   189  		if err == nil {
   190  			return
   191  		}
   192  	}
   193  	log.Printf("Error opening URL in browser.")
   194  }
   195  
   196  func valueOrFileContents(value string, filename string) string {
   197  	if value != "" {
   198  		return value
   199  	}
   200  	slurp, err := os.ReadFile(filename)
   201  	if err != nil {
   202  		log.Fatalf("Error reading %q: %v", filename, err)
   203  	}
   204  	return strings.TrimSpace(string(slurp))
   205  }
   206  

View as plain text