package main import ( "encoding/base64" "encoding/json" "net/http" "os" "strings" "github.com/gorilla/mux" ) func profileEndpoint(w http.ResponseWriter, r *http.Request) { uuid := mux.Vars(r)["uuid"] query := r.URL.Query() if len(uuid) == 32 { uuid = growUUID(uuid) } if len(uuid) != 36 { sendError(w, YggError{ Code: 400, Error: "Bad Request", ErrorMessage: "Invalid UUID.", }) return } exists, err := playerExistsByUUID(uuid) if err != nil { handleError(w, err) return } if !exists { sendError(w, YggError{ Code: 400, Error: "Bad Request", ErrorMessage: "The user does not exist.", }) return } player, err := getPlayerByUUID(uuid) if err != nil { handleError(w, err) return } signed := query.Has("unsigned") && query.Get("unsigned") == "false" response, err := generateProfileResponse(uuid, player.Username, signed) if err != nil { handleError(w, err) return } sendJSON(w, response) } func hasJoinedEndpoint(w http.ResponseWriter, r *http.Request) { params := r.URL.Query() if !params.Has("username") || !params.Has("serverId") { sendJSON(w, &Empty{}) return } player, err := getPlayerByUsername(params.Get("username")) if err != nil { handleError(w, err) return } response, err := generateProfileResponse(player.UUID, params.Get("username"), true) if err != nil { handleError(w, err) return } sendJSON(w, response) } func joinEndpoint(w http.ResponseWriter, r *http.Request) { var payload JoinPayload unmarshalTo(r, &payload) if payload.AccessToken == "" || payload.SelectedProfile == "" || payload.ServerId == "" { sendError(w, YggError{ Code: 400, Error: "IllegalArgumentException", ErrorMessage: "A required field is not present.", }) return } player, err := getPlayerByAuthToken(payload.AccessToken) if err != nil { handleError(w, err) return } if payload.SelectedProfile != shrinkUUID(player.UUID) { sendError(w, YggError{ Code: 400, Error: "Bad Request", ErrorMessage: "The request data is malformed.", }) return } sendEmpty(w) } func registerSessionEndpoints(r *mux.Router) { prefix := "/sessionserver" r.HandleFunc(prefix+"/session/minecraft/profile/{uuid}", profileEndpoint).Methods("GET") r.HandleFunc(prefix+"/session/minecraft/join", joinEndpoint).Methods("POST") r.HandleFunc(prefix+"/session/minecraft/hasJoined", hasJoinedEndpoint).Methods("GET") } func generateProfileResponse(uuid string, username string, signed bool) (ProfileResponse, error) { // todo: make this more visually appealing if possible skin := SkinTexture{} skin.Url = config.Protocol + config.BaseUrl + "/getTexture/" + uuid + "?type=skin" skin.Metadata = SkinMetadata{} skin.Metadata.Model = "default" textures := ProfileTextureMetadata{} textures.Id = shrinkUUID(uuid) textures.Name = username textures.Textures = ProfileTextures{} textures.Textures.Skin = skin _, err := os.Stat("capes/" + uuid + ".png") if err == nil { cape := &Texture{} cape.Url = config.Protocol + config.BaseUrl + "/getTexture/" + uuid + "?type=cape" textures.Textures.Cape = cape } marshalledTextures, err := json.Marshal(textures) if err != nil { return ProfileResponse{}, err } encodedTextures := base64.StdEncoding.EncodeToString(marshalledTextures) response := ProfileResponse{} response.Id = shrinkUUID(uuid) response.Name = username response.Properties = []Property{ { Name: "textures", Value: encodedTextures, }, } if signed && privateKey != nil { signedTextures, err := signWithPrivateKey(string(encodedTextures)) if err != nil { return ProfileResponse{}, err } b64 := make([]byte, base64.StdEncoding.EncodedLen(len(signedTextures))) base64.StdEncoding.Encode(b64, signedTextures) stringified := string(b64) response.Properties[0].Signature = &stringified } return response, nil } func shrinkUUID(uuid string) string { return strings.Join(strings.Split(uuid, "-"), "") } func growUUID(uuid string) string { if len(uuid) == 32 { return uuid[0:8] + "-" + uuid[8:12] + "-" + uuid[12:16] + "-" + uuid[16:20] + "-" + uuid[20:32] } return "" }