/* sourceHandler is the handler for icecast source clients. It acknowledges the client before sending it over to the icecast manager. */ func sourceHandler(w http.ResponseWriter, r *http.Request, clientID *ClientID) { /* Handler for icecast source requests. This can only be called by authenticated requests */ // Icecast clients expect a 200 OK response before sending data. w.WriteHeader(http.StatusOK) // Make sure to send the extra newline to signify end of headers io.WriteString(w, "\r\n") // And flush the data because buffering is FUN! if flush, ok := w.(http.Flusher); ok { flush.Flush() } // We can now start hijacking the connection hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Webserver doesn't support hijacking.", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Create a client struct, this is defined in client.go client := NewClient(conn, bufrw, clientID) ClientManager.Receiver <- client // The manager will handle everything from this point on return }
func listclientsHandler(w http.ResponseWriter, r *http.Request, clientID *ClientID) { response := []byte("<?xml version=\"1.0\"?>\n<icestats><source mount=\"" + clientID.Mount + "\"><Listeners>0</Listeners></source></icestats>\n") w.Header().Set("Content-Length", strconv.Itoa(len(response))) w.Write(response) }
/* adminHandler is called whenever the /admin URL is requested. The two URLs special cased for metadata and listener listing respectively are not included and are handled by different handlers. */ func adminHandler(w http.ResponseWriter, r *http.Request, clientID *ClientID) { HandlerLock.Lock() if r.URL.Path == "/admin" { Body := "" for mount, clients := range HandlerMounts { MountBody := "" for i, c := range clients { name := c.ClientID.Name if i == 0 { name = fmt.Sprintf("<b>%s</b>", c.ClientID.Name) } ClientBody := fmt.Sprintf(ClientHTML, name, c.Metadata, c.ClientID.Agent, mount, i, "") MountBody = MountBody + ClientBody } Body = Body + fmt.Sprintf(MountHTML, mount, MountBody) } w.Write([]byte(fmt.Sprintf(AdminHTML, Body))) } else if r.URL.Path == "/admin/kick" { MountName := r.URL.Query().Get("mount") Id, err := strconv.Atoi(r.URL.Query().Get("num")) if err == nil { clients, ok := HandlerMounts[MountName] if ok { clients[Id].Conn.Close() } } w.Header().Set("Location", "/admin") w.WriteHeader(301) } HandlerLock.Unlock() }
func metadataHandler(w http.ResponseWriter, r *http.Request, clientID *ClientID) { /* Handles a metadata request from a source. This should make sure an user cannot set the metadata of another users stream and even save metadata of inactive users */ var meta string parsed := r.URL.Query() meta = parsed.Get("song") if meta == "" { // Someone is trying to not update the metadata? // Ignore the f****r return } var charset string if e := parsed.Get("charset"); e != "" { // TODO: Check if user specified encodings are not vulnerable to exploits charset = e } else { charset = "latin1" } // This isn't so much a parser as it is a encoding handler. meta = ParseMetadata(charset, meta) // Sending empty metadata is useless, so we don't if meta != "" { // And we are done here, send the data we have so far along ClientManager.MetaChan <- &MetaPack{Data: meta, ID: clientID, Seen: false} } response := []byte("<?xml version=\"1.0\"?>\n<iceresponse><message>Metadata update successful</message><return>1</return></iceresponse>\n") w.Header().Set("Content-Length", strconv.Itoa(len(response))) w.Write(response) }
func AuthenticationError(w http.ResponseWriter, r *http.Request, err error) { /* Returns an authentication icecast error page when called. */ w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`) w.WriteHeader(401) response := "401 Unauthorized\n" if err != nil { response += err.Error() } w.Write([]byte(response)) }