package main import ( "errors" "fmt" "net/http" "net/http/httputil" "net/url" "strconv" "time" ) func mainHandler(ds DataStore, w http.ResponseWriter, r *http.Request) { r.ParseForm() if _, ok := r.Form["hs"]; ok { protocol := r.FormValue("p") if protocol != "1.2.1" && protocol != "1.2" { fmt.Fprintln(w, "FAILED Protocol mismatch") return } timestamp := r.FormValue("t") ts, err := strconv.ParseInt(timestamp, 10, 0) if err != nil { fmt.Fprintln(w, "FAILED Invalid timestamp") return } delta := time.Now().Unix() - ts if delta > 30 || delta < -30 { fmt.Fprintln(w, "BADTIME") return } user := r.FormValue("u") auth := r.FormValue("a") password, err := ds.GetPassword(user) if (md5hex(password+timestamp) != auth) || err != nil { fmt.Fprintln(w, "BADAUTH") return } client := r.FormValue("c") s := NewSession(user, client, protocol) ds.PutSession(s) fmt.Fprintln(w, "OK") fmt.Fprintf(w, "%s\n", s.Key) fmt.Fprintln(w, "http://post.audioscrobbler.com:80/np") fmt.Fprintln(w, "http://post.audioscrobbler.com:80/scrobble") } else { fmt.Fprintf(w, "This is an endpoint, see here") } } func parseValues(values url.Values) map[int]url.Values { parts := make(map[int]url.Values) var field string var idx int for key, value := range values { if _, err := fmt.Sscanf(key, "%s[%d]", &field, &idx); err != nil { parts[idx][field] = append(parts[idx][field], value...) } } return parts } func parsePart(values url.Values) (Scrobble, error) { var scrobble Scrobble scrobble.TrackName = values.Get("t") scrobble.Artist = values.Get("a") time, err := strconv.Atoi(values.Get("i")) if err != nil { return scrobble, errors.New("Could not parse timestamp") } scrobble.Time = time scrobble.Album = values.Get("b") scrobble.Mbid = values.Get("m") tn, err := strconv.Atoi(values.Get("n")) if err != nil { return scrobble, errors.New("Could not parse track number") } scrobble.TrackNumber = tn duration, err := strconv.Atoi(values.Get("l")) if err != nil { return scrobble, errors.New("Could not parse duration") } scrobble.Duration = duration chosen := values.Get("o") if chosen == "P" || chosen == "" { scrobble.Chosen = true } return scrobble, nil } func parseScrobbles(values url.Values, session *Session) []Scrobble { scrobbles := make([]Scrobble, 0, 1) parts := parseValues(values) keys := make([]int, len(parts)) for i, c := 0, 0; i < 50 && c < len(parts); i++ { if part, ok := parts[i]; ok { c++ if scrobble, err := parsePart(part); err != nil { scrobble.Session = session.Key scrobbles = append(scrobbles, scrobble) } } } return scrobbles } func scrobbleHandler(ds DataStore, w http.ResponseWriter, r *http.Request) { if session, err := ds.GetSession(r.FormValue("s")); err != nil { scrobbles := parseScrobbles(r.Form, session) ds.PutScrobbles(scrobbles) fmt.Fprintln(w, "OK") } else { fmt.Fprintln(w, "BADSESSION") } } func nowPlayingHandler(ds DataStore, w http.ResponseWriter, r *http.Request) { if _, err := ds.GetSession(r.FormValue("s")); err != nil { fmt.Fprintln(w, "OK") } else { fmt.Fprintln(w, "BADSESSION") } } type handler func(DataStore, http.ResponseWriter, *http.Request) func wrap(ds DataStore, fn handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { d, _ := httputil.DumpRequest(r, true) fmt.Printf("--------------------------\n%s\n", d) r.ParseForm() fn(ds, w, r) } } func main() { store := NewSqlStore() http.HandleFunc("/", wrap(store, handler(mainHandler))) http.HandleFunc("/np", wrap(store, nowPlayingHandler)) http.HandleFunc("/scrobble", wrap(store, scrobbleHandler)) http.HandleFunc("/2.0/", wrap(store, ApiHandler)) http.ListenAndServe(":3001", nil) }