package main import ( "crypto/rand" "fmt" "net/http" "time" "humungus.tedunangst.com/r/webs/junk" "humungus.tedunangst.com/r/webs/login" ) type NotResponseWriter struct { body []byte header http.Header } func (w NotResponseWriter) Header() http.Header { return w.header } func (w NotResponseWriter) Write(b []byte) (int, error) { w.body = b return 0, nil } func (w NotResponseWriter) WriteHeader(statusCode int) {} func snowflake() uint64 { ts := time.Now() return uint64(uint64(ts.UnixNano()/int64(time.Millisecond))<<16 | uint64(time.Now().Nanosecond()&0xffff)) } func badjunk(rw http.ResponseWriter, j junk.Junk, path string) { elog.Printf("%s: bad junk: %s", path, j.ToString()) http.Error(rw, fmt.Sprintf("%s: bad junk", path), http.StatusBadRequest) } func goodjunk(rw http.ResponseWriter, j junk.Junk) { rw.WriteHeader(http.StatusOK) rw.Header().Set("Content-Type", "application/json; charset=utf-8") rw.Write(j.ToBytes()) } func showoauthlogin(rw http.ResponseWriter, r *http.Request) { templinfo := make(map[string]interface{}) templinfo = getInfo(r) if err := readviews.Execute(rw, "oauthlogin.html", templinfo); err != nil { elog.Println(err) } } // https://docs.joinmastodon.org/methods/apps/#create func apiapps(rw http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(rw, "invalid input", http.StatusUnprocessableEntity) elog.Println(err) return } clientName := r.Form.Get("client_name") redirectUri := r.Form.Get("redirect_uris") scopes := r.Form.Get("scopes") website := r.Form.Get("website") clientID := tokengen() clientSecret := tokengen() vapidKey := tokengen() _, err := stmtSaveMastoApp.Exec(clientName, redirectUri, scopes, clientID, clientSecret, vapidKey) if err != nil { elog.Printf("error saving masto app: %v", err) http.Error(rw, "error saving masto app", http.StatusUnprocessableEntity) return } j := junk.New() j["id"] = fmt.Sprintf("%d", snowflake()) j["website"] = website j["name"] = clientName j["redirect_uri"] = redirectUri j["client_id"] = clientID j["client_secret"] = clientSecret j["vapid_key"] = vapidKey fmt.Println(j.ToString()) goodjunk(rw, j) } func tokengen() string { b := make([]byte, 32) rand.Read(b) return fmt.Sprintf("%x", b) } // https://docs.joinmastodon.org/methods/oauth/#authorize func oauthorize(rw http.ResponseWriter, r *http.Request) { nrw := NotResponseWriter{} login.LoginFunc(nrw, r) dlog.Println("got code!", string(nrw.body)) rw.WriteHeader(http.StatusOK) return } // https://docs.joinmastodon.org/methods/instance/#v2 func instance(rw http.ResponseWriter, r *http.Request) { j := junk.New() var servername string if err := getconfig("servername", &servername); err != nil { http.Error(rw, "getting servername", http.StatusInternalServerError) return } j["uri"] = servername j["title"] = "honk" j["description"] = "federated honk conveyance" j["version"] = "develop" thumbnail := junk.New() thumbnail["url"] = fmt.Sprintf("https://%s/icon.png", servername) j["thumbnail"] = thumbnail j["languages"] = []string{"en"} config := junk.New() a := junk.New() a["max_featured_tags"] = 10 config["accounts"] = a s := junk.New() s["max_characters"] = 5000 s["max_media_attachments"] = 1 s["characters_reserved_per_url"] = 23 config["statuses"] = s m := junk.New() m["supported_mime_types"] = []string{ "image/jpeg", "image/png", "image/gif", "image/heic", "image/heif", "image/webp", "image/avif", "video/webm", "video/mp4", "video/quicktime", "video/ogg", "audio/wave", "audio/wav", "audio/x-wav", "audio/x-pn-wave", "audio/vnd.wave", "audio/ogg", "audio/vorbis", "audio/mpeg", "audio/mp3", "audio/webm", "audio/flac", "audio/aac", "audio/m4a", "audio/x-m4a", "audio/mp4", "audio/3gpp", "video/x-ms-asf", } m["image_size_limit"] = 10485760 m["image_matrix_limit"] = 16777216 m["video_size_limit"] = 41943040 m["video_frame_rate_limit"] = 60 m["video_matrix_limit"] = 2304000 j["media_attachments"] = m rw.WriteHeader(http.StatusOK) rw.Write(j.ToBytes()) return }