package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "log" "net/http" "os" "path/filepath" "gopkg.in/yaml.v3" ) var ConfigFn = filepath.Join(os.Getenv("HOME"), ".paste") var TokenFN = filepath.Join(os.Getenv("HOME"), ".paste-token") var logger = log.New(os.Stderr, "", 0) var httpC = &http.Client{} type Paste struct { Id string Title string Tags map[string]struct{} Content string } type Config struct { Username string Password string Hostname string } func loadConfig(fn string) (*Config, error) { fh, err := os.Open(fn) if err != nil { return nil, err } dec := yaml.NewDecoder(fh) dec.KnownFields(true) c := &Config{} err = dec.Decode(c) return c, err } func (c *Config) NoTokenReq(method, pth string, data interface{}) (*http.Response, error) { b, err := json.Marshal(data) if err != nil { return nil, err } buf := bytes.NewBuffer(b) req, err := http.NewRequest(method, c.Hostname+pth, buf) if err != nil { logger.Fatal(err) } req.Header.Add("Content-type", "application/json") resp, err := httpC.Do(req) if err != nil { logger.Fatal(err) } return resp, err } func (c *Config) Req(method, pth string, data interface{}) (*http.Response, error) { b, err := json.Marshal(data) if err != nil { return nil, err } buf := bytes.NewBuffer(b) req, err := http.NewRequest(method, c.Hostname+pth, buf) if err != nil { logger.Fatal(err) } req.Header.Add("Authorization", "Bearer "+c.Token()) req.Header.Add("Content-type", "application/json") resp, err := httpC.Do(req) if err != nil { logger.Fatal(err) } return resp, err } func (c *Config) Token() string { s, err := c.GetToken() if err != nil { logger.Fatal(err) } return s } func (c *Config) GetToken() (string, error) { resp, err := c.NoTokenReq("POST", "/login", map[string]string{ "Username": c.Username, "Password": c.Password, }) if err != nil { return "", err } respData := map[string]string{} dec := json.NewDecoder(resp.Body) err = dec.Decode(&respData) if err != nil { return "", err } token, ok := respData["token"] if !ok { logger.Fatal("Cannot have empty token") } // logger.Println(token) return token, nil } func (c *Config) NewPaste(title string) { buf := &bytes.Buffer{} _, err := io.Copy(buf, os.Stdin) if err != nil { logger.Fatal(err) } p := &Paste{ Title: title, Content: buf.String(), } resp, err := c.Req("POST", "/new", p) if err != nil { logger.Fatal(err) } if resp.StatusCode != 200 { io.Copy(os.Stderr, resp.Body) os.Exit(1) } out := map[string]string{} dec := json.NewDecoder(resp.Body) err = dec.Decode(&out) if err != nil { logger.Println(err) } fmt.Printf("%s/view/%s\n", c.Hostname, out["id"]) } func main() { fl := flag.NewFlagSet("simple pastebin client", flag.ExitOnError) fl.StringVar(&ConfigFn, "c", ConfigFn, "Configuration file") title := fl.String("t", "", "Optional title for the message") debug := fl.Bool("d", false, "debugging add information to the logging output DEBUG=true|false controls this as well") _ = fl.Parse(os.Args[1:]) if d := os.Getenv("DEBUG"); d == "true" || *debug { logger.SetFlags(log.LstdFlags | log.Llongfile) } conf, err := loadConfig(ConfigFn) if err != nil { logger.Println(err) fmt.Println("") fmt.Println("//") fmt.Println("// Example configuration file") enc := yaml.NewEncoder(os.Stdout) enc.SetIndent(2) enc.Encode(&Config{ Username: "changeme", Password: "changeme", Hostname: "https://example.com/paste", }) os.Exit(1) } conf.NewPaste(*title) }