-
Notifications
You must be signed in to change notification settings - Fork 2
/
pinboard.go
93 lines (81 loc) · 2.51 KB
/
pinboard.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Package pinboard is an implementation of the Pinboard V1 API (https://pinboard.in/api)
//
// This package implements the API as documented, though some fixes have been made to
// maintain type cohesion. See method comments for exceptions to the API documentation.
package pinboard
import (
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
var apiBase = "https://api.pinboard.in/v1/"
// A Pinboard represents a client for the Pinboard V1 API. Authentication can use
// passwords or tokens. Token auth is recommended for good password hygiene.
type Pinboard struct {
User string
Password string
Token string
}
func (p *Pinboard) authQuery(u *url.URL) error {
if p == nil {
return fmt.Errorf("Pinboard object has not been initialized")
}
if len(p.User) < 1 {
return fmt.Errorf("Pinboard requires a Username and either a Password or Token for authentication")
}
if len(p.Token) < 1 {
if len(p.Password) < 1 {
return fmt.Errorf("Pinboard requires either a Password or Token for authentication")
}
u.User = url.UserPassword(p.User, p.Password)
return nil
}
q := u.Query()
q.Set("auth_token", fmt.Sprintf("%s:%s", p.User, p.Token))
u.RawQuery = q.Encode()
return nil
}
// Retrieve an API response for the given URL. Auth is added to the URL object here
func (p *Pinboard) get(u *url.URL) (*http.Response, error) {
err := p.authQuery(u)
if err != nil {
return nil, fmt.Errorf("Pinboard failed to generate an auth query param: %v", err)
}
resp, err := http.Get(u.String())
if err != nil {
return nil, err
}
if resp.StatusCode >= 400 {
if resp.StatusCode == 401 {
return nil, fmt.Errorf("Error from Pinboard API: Authentication Failed")
}
resp_body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Error reading error body: %v", err)
}
resp.Body.Close()
return nil, fmt.Errorf("Error from Pinboard API (%d): %v", resp.StatusCode, string(resp_body))
}
return resp, err
}
// parseResponse is a helper for parsing XML into different types. The use of the
// empty interface and type assertions may be too clever for our own good. Feel free
// to report bugs if you get panics from this
func parseResponse(resp *http.Response, to interface{}) (interface{}, error) {
resp_body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
err = xml.Unmarshal(resp_body, to)
if err != nil {
return nil, err
}
return to, nil
}
type result struct {
XMLName xml.Name `xml:"result" json:"-"`
Result string `xml:",innerxml"`
}