masto.go (view raw)
1package main
2
3import (
4 "database/sql"
5 "fmt"
6 "log"
7 "net/http"
8
9 "humungus.tedunangst.com/r/webs/junk"
10 "humungus.tedunangst.com/r/webs/login"
11)
12
13type MastoApp struct {
14 Name string `db:"clientname"`
15 RedirectURI string `db:"redirecturi"`
16 ClientID string `db:"clientid"`
17 ClientSecret string `db:"clientsecret"`
18 VapidKey string `db:"vapidkey"`
19 AuthToken string `db:"authtoken"`
20 Scopes string `db:"scopes"`
21}
22
23func showoauthlogin(rw http.ResponseWriter, r *http.Request) {
24 templinfo := make(map[string]interface{})
25 templinfo = getInfo(r)
26 templinfo["ClientID"] = r.URL.Query().Get("client_id")
27 templinfo["RedirectURI"] = r.URL.Query().Get("redirect_uri")
28
29 if err := readviews.Execute(rw, "oauthlogin.html", templinfo); err != nil {
30 elog.Println(err)
31 }
32}
33
34// https://docs.joinmastodon.org/methods/apps/#create
35func apiapps(rw http.ResponseWriter, r *http.Request) {
36 if err := r.ParseForm(); err != nil {
37 http.Error(rw, "invalid input", http.StatusUnprocessableEntity)
38 elog.Println(err)
39 return
40 }
41 clientName := r.Form.Get("client_name")
42 redirectURI := r.Form.Get("redirect_uris")
43 scopes := r.Form.Get("scopes")
44 website := r.Form.Get("website")
45 clientID := tokengen()
46 clientSecret := tokengen()
47 vapidKey := tokengen()
48
49 _, err := stmtSaveMastoApp.Exec(clientName, redirectURI, scopes, clientID, clientSecret, vapidKey, "")
50 if err != nil {
51 elog.Printf("error saving masto app: %v", err)
52 http.Error(rw, "error saving masto app", http.StatusUnprocessableEntity)
53 return
54 }
55
56 j := junk.New()
57 j["id"] = "19"
58 j["website"] = website
59 j["name"] = clientName
60 j["redirect_uri"] = redirectURI
61 j["client_id"] = clientID
62 j["client_secret"] = clientSecret
63 j["vapid_key"] = vapidKey
64
65 fmt.Println(j.ToString())
66 goodjunk(rw, j)
67}
68
69// https://docs.joinmastodon.org/methods/oauth/#authorize
70func oauthorize(rw http.ResponseWriter, r *http.Request) {
71 clientID := r.FormValue("client_id")
72 redirectURI := r.FormValue("redirect_uri")
73
74 if !checkClientID(clientID) {
75 elog.Println("oauth: no such client:", clientID)
76 rw.WriteHeader(http.StatusUnauthorized)
77 return
78 }
79
80 var nrw NotResponseWriter
81 login.LoginFunc(&nrw, r)
82
83 _, err := stmtSaveMastoAppToken.Exec(nrw.auth)
84 if err != nil {
85 elog.Println("oauth: failed to save masto app token", err)
86 rw.WriteHeader(http.StatusInternalServerError)
87 return
88 }
89
90 uri := fmt.Sprintf("%s?code=%s", redirectURI, nrw.auth)
91
92 log.Println("redirecting to", uri)
93 rw.Header().Set("Content-Type", "")
94 rw.Header().Set("Location", uri)
95 rw.WriteHeader(302)
96}
97
98// https://docs.joinmastodon.org/methods/oauth/#token
99func oauthtoken(rw http.ResponseWriter, r *http.Request) {
100 // grantType := r.FormValue("grant_type")
101 // code := r.FormValue("code")
102 clientID := r.FormValue("client_id")
103 clientSecret := r.FormValue("client_Secret")
104 // redirectURI := r.FormValue("redirect_uri")
105 // gotScopes := r.FormValue("scopes")
106
107 if !checkClient(clientID, clientSecret) {
108 elog.Println("oauth: no such client:", clientID)
109 rw.WriteHeader(http.StatusUnauthorized)
110 return
111 }
112
113 app := MastoApp{}
114 row := stmtGetMastoApp.QueryRowx(clientID)
115 err := row.StructScan(&app)
116 if err == sql.ErrNoRows {
117 elog.Println("oauth: invalid client", clientID)
118 rw.WriteHeader(http.StatusUnauthorized)
119 return
120 }
121
122 fmt.Printf("%#v", app)
123}
124
125// https://docs.joinmastodon.org/methods/instance/#v2
126func instance(rw http.ResponseWriter, r *http.Request) {
127 j := junk.New()
128
129 var servername string
130 if err := getconfig("servername", &servername); err != nil {
131 http.Error(rw, "getting servername", http.StatusInternalServerError)
132 return
133 }
134
135 j["uri"] = servername
136 j["title"] = "honk"
137 j["description"] = "federated honk conveyance"
138 j["version"] = "develop"
139
140 thumbnail := junk.New()
141 thumbnail["url"] = fmt.Sprintf("https://%s/icon.png", servername)
142 j["thumbnail"] = thumbnail
143 j["languages"] = []string{"en"}
144
145 config := junk.New()
146
147 a := junk.New()
148 a["max_featured_tags"] = 10
149 config["accounts"] = a
150
151 s := junk.New()
152 s["max_characters"] = 5000
153 s["max_media_attachments"] = 1
154 s["characters_reserved_per_url"] = 23
155 config["statuses"] = s
156
157 m := junk.New()
158 m["supported_mime_types"] = []string{
159 "image/jpeg",
160 "image/png",
161 "image/gif",
162 "image/heic",
163 "image/heif",
164 "image/webp",
165 "image/avif",
166 "video/webm",
167 "video/mp4",
168 "video/quicktime",
169 "video/ogg",
170 "audio/wave",
171 "audio/wav",
172 "audio/x-wav",
173 "audio/x-pn-wave",
174 "audio/vnd.wave",
175 "audio/ogg",
176 "audio/vorbis",
177 "audio/mpeg",
178 "audio/mp3",
179 "audio/webm",
180 "audio/flac",
181 "audio/aac",
182 "audio/m4a",
183 "audio/x-m4a",
184 "audio/mp4",
185 "audio/3gpp",
186 "video/x-ms-asf",
187 }
188
189 m["image_size_limit"] = 10485760
190 m["image_matrix_limit"] = 16777216
191 m["video_size_limit"] = 41943040
192 m["video_frame_rate_limit"] = 60
193 m["video_matrix_limit"] = 2304000
194 j["media_attachments"] = m
195
196 goodjunk(rw, j)
197
198 return
199}