2020-01-02 02:12:45 +05:30
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
|
|
|
|
"github.com/elyby/chrly/api/mojang"
|
|
|
|
"github.com/elyby/chrly/model"
|
|
|
|
)
|
|
|
|
|
|
|
|
type SkinsRepository interface {
|
2020-04-21 00:48:27 +05:30
|
|
|
FindSkinByUsername(username string) (*model.Skin, error)
|
|
|
|
FindSkinByUserId(id int) (*model.Skin, error)
|
|
|
|
SaveSkin(skin *model.Skin) error
|
|
|
|
RemoveSkinByUserId(id int) error
|
|
|
|
RemoveSkinByUsername(username string) error
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
type CapesRepository interface {
|
2020-04-21 00:48:27 +05:30
|
|
|
FindCapeByUsername(username string) (*model.Cape, error)
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
type MojangTexturesProvider interface {
|
|
|
|
GetForUsername(username string) (*mojang.SignedTexturesResponse, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Skinsystem struct {
|
2020-01-29 04:04:15 +05:30
|
|
|
Emitter
|
|
|
|
SkinsRepo SkinsRepository
|
|
|
|
CapesRepo CapesRepository
|
|
|
|
MojangTexturesProvider MojangTexturesProvider
|
2020-04-19 05:01:09 +05:30
|
|
|
TexturesExtraParamName string
|
|
|
|
TexturesExtraParamValue string
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
func (ctx *Skinsystem) Handler() *mux.Router {
|
2020-01-02 02:12:45 +05:30
|
|
|
router := mux.NewRouter().StrictSlash(true)
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
router.HandleFunc("/skins/{username}", ctx.skinHandler).Methods(http.MethodGet)
|
|
|
|
router.HandleFunc("/cloaks/{username}", ctx.capeHandler).Methods(http.MethodGet).Name("cloaks")
|
|
|
|
router.HandleFunc("/textures/{username}", ctx.texturesHandler).Methods(http.MethodGet)
|
|
|
|
router.HandleFunc("/textures/signed/{username}", ctx.signedTexturesHandler).Methods(http.MethodGet)
|
2020-01-02 02:12:45 +05:30
|
|
|
// Legacy
|
2020-04-19 05:01:09 +05:30
|
|
|
router.HandleFunc("/skins", ctx.skinGetHandler).Methods(http.MethodGet)
|
|
|
|
router.HandleFunc("/cloaks", ctx.capeGetHandler).Methods(http.MethodGet)
|
2020-01-02 02:12:45 +05:30
|
|
|
|
|
|
|
return router
|
|
|
|
}
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
func (ctx *Skinsystem) skinHandler(response http.ResponseWriter, request *http.Request) {
|
2020-01-02 02:12:45 +05:30
|
|
|
username := parseUsername(mux.Vars(request)["username"])
|
2020-04-21 00:48:27 +05:30
|
|
|
rec, err := ctx.SkinsRepo.FindSkinByUsername(username)
|
2020-04-20 17:46:15 +05:30
|
|
|
if err == nil && rec != nil && rec.SkinId != 0 {
|
2020-01-02 02:12:45 +05:30
|
|
|
http.Redirect(response, request, rec.Url, 301)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mojangTextures, err := ctx.MojangTexturesProvider.GetForUsername(username)
|
|
|
|
if err != nil || mojangTextures == nil {
|
|
|
|
response.WriteHeader(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
texturesProp := mojangTextures.DecodeTextures()
|
|
|
|
skin := texturesProp.Textures.Skin
|
|
|
|
if skin == nil {
|
|
|
|
response.WriteHeader(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
http.Redirect(response, request, skin.Url, 301)
|
|
|
|
}
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
func (ctx *Skinsystem) skinGetHandler(response http.ResponseWriter, request *http.Request) {
|
2020-01-02 02:12:45 +05:30
|
|
|
username := request.URL.Query().Get("name")
|
|
|
|
if username == "" {
|
|
|
|
response.WriteHeader(http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mux.Vars(request)["username"] = username
|
|
|
|
mux.Vars(request)["converted"] = "1"
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
ctx.skinHandler(response, request)
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
func (ctx *Skinsystem) capeHandler(response http.ResponseWriter, request *http.Request) {
|
2020-01-02 02:12:45 +05:30
|
|
|
username := parseUsername(mux.Vars(request)["username"])
|
2020-04-21 00:48:27 +05:30
|
|
|
rec, err := ctx.CapesRepo.FindCapeByUsername(username)
|
2020-04-20 17:46:15 +05:30
|
|
|
if err == nil && rec != nil {
|
2020-01-02 02:12:45 +05:30
|
|
|
request.Header.Set("Content-Type", "image/png")
|
|
|
|
_, _ = io.Copy(response, rec.File)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mojangTextures, err := ctx.MojangTexturesProvider.GetForUsername(username)
|
|
|
|
if err != nil || mojangTextures == nil {
|
|
|
|
response.WriteHeader(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
texturesProp := mojangTextures.DecodeTextures()
|
|
|
|
cape := texturesProp.Textures.Cape
|
|
|
|
if cape == nil {
|
|
|
|
response.WriteHeader(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
http.Redirect(response, request, cape.Url, 301)
|
|
|
|
}
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
func (ctx *Skinsystem) capeGetHandler(response http.ResponseWriter, request *http.Request) {
|
2020-01-02 02:12:45 +05:30
|
|
|
username := request.URL.Query().Get("name")
|
|
|
|
if username == "" {
|
|
|
|
response.WriteHeader(http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mux.Vars(request)["username"] = username
|
|
|
|
mux.Vars(request)["converted"] = "1"
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
ctx.capeHandler(response, request)
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
func (ctx *Skinsystem) texturesHandler(response http.ResponseWriter, request *http.Request) {
|
2020-01-02 02:12:45 +05:30
|
|
|
username := parseUsername(mux.Vars(request)["username"])
|
|
|
|
|
|
|
|
var textures *mojang.TexturesResponse
|
2020-04-21 00:48:27 +05:30
|
|
|
skin, skinErr := ctx.SkinsRepo.FindSkinByUsername(username)
|
|
|
|
cape, capeErr := ctx.CapesRepo.FindCapeByUsername(username)
|
2020-04-20 17:46:15 +05:30
|
|
|
if (skinErr == nil && skin != nil && skin.SkinId != 0) || (capeErr == nil && cape != nil) {
|
2020-01-02 02:12:45 +05:30
|
|
|
textures = &mojang.TexturesResponse{}
|
2020-04-20 17:46:15 +05:30
|
|
|
if skinErr == nil && skin != nil && skin.SkinId != 0 {
|
2020-01-02 02:12:45 +05:30
|
|
|
skinTextures := &mojang.SkinTexturesResponse{
|
|
|
|
Url: skin.Url,
|
|
|
|
}
|
|
|
|
|
|
|
|
if skin.IsSlim {
|
|
|
|
skinTextures.Metadata = &mojang.SkinTexturesMetadata{
|
|
|
|
Model: "slim",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
textures.Skin = skinTextures
|
|
|
|
}
|
|
|
|
|
2020-04-20 17:46:15 +05:30
|
|
|
if capeErr == nil && cape != nil {
|
2020-01-02 02:12:45 +05:30
|
|
|
textures.Cape = &mojang.CapeTexturesResponse{
|
|
|
|
Url: request.URL.Scheme + "://" + request.Host + "/cloaks/" + username,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mojangTextures, err := ctx.MojangTexturesProvider.GetForUsername(username)
|
|
|
|
if err != nil || mojangTextures == nil {
|
|
|
|
response.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
texturesProp := mojangTextures.DecodeTextures()
|
|
|
|
if texturesProp == nil {
|
2020-02-16 15:53:47 +05:30
|
|
|
ctx.Emit("skinsystem:error", errors.New("unable to find textures property"))
|
2020-01-29 04:04:15 +05:30
|
|
|
apiServerError(response)
|
2020-01-02 02:12:45 +05:30
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
textures = texturesProp.Textures
|
2020-04-20 22:28:31 +05:30
|
|
|
if textures.Skin == nil && textures.Cape == nil {
|
|
|
|
response.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
responseData, _ := json.Marshal(textures)
|
|
|
|
response.Header().Set("Content-Type", "application/json")
|
2020-01-05 23:09:17 +05:30
|
|
|
_, _ = response.Write(responseData)
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
2020-04-19 05:01:09 +05:30
|
|
|
func (ctx *Skinsystem) signedTexturesHandler(response http.ResponseWriter, request *http.Request) {
|
2020-01-02 02:12:45 +05:30
|
|
|
username := parseUsername(mux.Vars(request)["username"])
|
|
|
|
|
|
|
|
var responseData *mojang.SignedTexturesResponse
|
|
|
|
|
2020-04-21 00:48:27 +05:30
|
|
|
rec, err := ctx.SkinsRepo.FindSkinByUsername(username)
|
2020-04-20 17:46:15 +05:30
|
|
|
if err == nil && rec != nil && rec.SkinId != 0 && rec.MojangTextures != "" {
|
2020-01-02 02:12:45 +05:30
|
|
|
responseData = &mojang.SignedTexturesResponse{
|
|
|
|
Id: strings.Replace(rec.Uuid, "-", "", -1),
|
|
|
|
Name: rec.Username,
|
|
|
|
Props: []*mojang.Property{
|
|
|
|
{
|
|
|
|
Name: "textures",
|
|
|
|
Signature: rec.MojangSignature,
|
|
|
|
Value: rec.MojangTextures,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
} else if request.URL.Query().Get("proxy") != "" {
|
|
|
|
mojangTextures, err := ctx.MojangTexturesProvider.GetForUsername(username)
|
|
|
|
if err == nil && mojangTextures != nil {
|
|
|
|
responseData = mojangTextures
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if responseData == nil {
|
|
|
|
response.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
responseData.Props = append(responseData.Props, &mojang.Property{
|
2020-04-20 18:10:20 +05:30
|
|
|
Name: ctx.TexturesExtraParamName,
|
|
|
|
Value: ctx.TexturesExtraParamValue,
|
2020-01-02 02:12:45 +05:30
|
|
|
})
|
|
|
|
|
|
|
|
responseJson, _ := json.Marshal(responseData)
|
|
|
|
response.Header().Set("Content-Type", "application/json")
|
2020-01-05 23:09:17 +05:30
|
|
|
_, _ = response.Write(responseJson)
|
2020-01-02 02:12:45 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
func parseUsername(username string) string {
|
|
|
|
return strings.TrimSuffix(username, ".png")
|
|
|
|
}
|