go/pastee.go

222 lines
4.3 KiB
Go

package pastee
import (
"bytes"
"encoding/json"
"errors"
"io"
"math/rand"
"net/http"
"strconv"
"time"
"paste.ee/go/cryptojs"
)
func New(key string) *Pastee {
return &Pastee{ApiKey: key, Base: "https://api.paste.ee/v1", Client: &http.Client{Timeout: time.Minute}}
}
func (p *Pastee) Authenticate(username, password string) (*AuthResponse, error) {
body, err := json.Marshal(&authRequest{username, password})
if err != nil {
return nil, err
}
req, err := p.newRequest("POST", "users/authenticate", bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Content-Length", strconv.Itoa(len(body)))
res, err := p.Client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var response AuthResponse
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, err
}
return &response, nil
}
func (p *Pastee) List(opts ...PasteListOptions) (*PasteListResponse, error) {
urlPath := "pastes"
if len(opts) > 0 {
urlPath += "?" + opts[0].encode()
}
req, err := p.newRequest("GET", urlPath, nil)
if err != nil {
return nil, err
}
res, err := p.Client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var response PasteListResponse
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, err
}
return &response, nil
}
func (p *Pastee) Get(id string, opts ...PasteGetOptions) (*Paste, error) {
urlPath := "pastes/" + id
if len(opts) > 0 {
urlPath += "?" + opts[0].encode()
}
req, err := p.newRequest("GET", urlPath, nil)
if err != nil {
return nil, err
}
res, err := p.Client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var response PasteGetResponse
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, err
}
if response.Success && response.Paste != nil {
if response.Paste.Encrypted && len(opts) > 0 && opts[0].Key != "" {
for _, section := range response.Paste.Sections {
b, err := cryptojs.Decrypt(section.Contents, opts[0].Key)
if err != nil {
return nil, err
}
section.Contents = string(b)
}
}
} else if len(response.Errors) > 0 {
return nil, errors.New("error: " + response.Errors[0].Message)
} else {
return nil, errors.New("unknown error")
}
return response.Paste, nil
}
func (p *Pastee) Submit(paste *Paste) (*PasteResponse, error) {
var key string
if paste.Encrypted {
key = RandStringBytesMaskImprSrc(32)
var err error
for _, section := range paste.Sections {
section.Contents, err = cryptojs.Encrypt(section.Contents, key)
if err != nil {
return nil, err
}
}
}
body, err := json.Marshal(paste)
if err != nil {
return nil, err
}
req, err := p.newRequest("POST", "pastes", bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Content-Length", strconv.Itoa(len(body)))
res, err := p.Client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var response PasteResponse
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, err
}
if key != "" {
response.Key = key
}
return &response, nil
}
func (p *Pastee) newRequest(method, path string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(method, p.Base+"/"+path, body)
if err != nil {
return nil, err
}
req.Header.Set("X-Auth-Token", p.ApiKey)
return req, nil
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var src = rand.NewSource(time.Now().UnixNano())
func RandStringBytesMaskImprSrc(n int) string {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}