package main import ( "errors" "fmt" "net/http" "net/http/httputil" "net/url" "sort" "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.Fprintf(w, "FAILED Protocol mismatch\n") return } timestamp := r.FormValue("t") ts, err := strconv.ParseInt(timestamp, 10, 0) if err != nil { fmt.Fprintf(w, "FAILED Invalid timestamp\n") return } delta := time.Now().Unix() - ts if delta > 30 || delta < -30 { fmt.Fprintf(w, "BADTIME\n") return } user := r.FormValue("u") auth := r.FormValue("a") password, err := ds.GetPassword(user) if (md5hex(password+timestamp) != auth) || err != nil { fmt.Fprintf(w, "BADAUTH\n") return } client := r.FormValue("c") s := NewSession(user, client, protocol) ds.PutSession(s) fmt.Fprint(w, "OK\n") fmt.Fprintf(w, "%s\n", s.Key) fmt.Fprint(w, "http://post.audioscrobbler.com:80/np\n") fmt.Fprint(w, "http://post.audioscrobbler.com:80/scrobble\n") } 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 letter string var idx int for key, value := range values { _, err := fmt.Sscanf(key, "%1s[%d]", &letter, &idx) if err != nil { continue } if _, ok := parts[idx]; !ok { parts[idx] = make(url.Values) } parts[idx][letter] = 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)) i := 0 for key := range parts { keys[i] = key i++ } sort.Ints(keys) for _, key := range keys { scrobble, err := parsePart(parts[key]) if err != nil { continue } 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.Fprintf(w, "OK\n") } else { fmt.Fprintf(w, "BADSESSION\n") } } 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.Fprintf(w, "BADSESSION\n") } } 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) }