001/*
002 * Copyright (C) 2026 Blackilykat and contributors
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program. If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package dev.blackilykat.pmp.server.handlers;
019
020import dev.blackilykat.pmp.MessageHandler;
021import dev.blackilykat.pmp.PMPConnection;
022import dev.blackilykat.pmp.messages.LoginAsExistingDeviceRequest;
023import dev.blackilykat.pmp.messages.LoginFailResponse;
024import dev.blackilykat.pmp.messages.LoginSuccessResponse;
025import dev.blackilykat.pmp.server.ClientConnection;
026import dev.blackilykat.pmp.server.Device;
027import dev.blackilykat.pmp.server.Playback;
028import dev.blackilykat.pmp.server.ServerStorage;
029import org.apache.logging.log4j.LogManager;
030import org.apache.logging.log4j.Logger;
031
032import java.util.List;
033
034public class LoginAsExistingDeviceRequestHandler extends MessageHandler<LoginAsExistingDeviceRequest> {
035        private static final Logger LOGGER = LogManager.getLogger(LoginAsExistingDeviceRequestHandler.class);
036
037        public LoginAsExistingDeviceRequestHandler() {
038                super(LoginAsExistingDeviceRequest.class);
039        }
040
041        @Override
042        public void run(PMPConnection pmpConnection, LoginAsExistingDeviceRequest message) {
043                ClientConnection connection = (ClientConnection) pmpConnection;
044
045                if(message.password == null && message.token == null) {
046                        connection.send(new LoginFailResponse(message.requestId, LoginFailResponse.Reason.BAD_REQUEST));
047                }
048
049                List<Device> devices = ServerStorage.SENSITIVE.devices.get();
050
051                for(Device device : devices) {
052                        if(device.id == message.deviceId) {
053                                if(device.getClientConnection() != null) {
054                                        connection.send(new LoginFailResponse(message.requestId,
055                                                        LoginFailResponse.Reason.DEVICE_ALREADY_CONNECTED));
056                                        return;
057                                }
058                                if((message.password != null && message.password.equals(ServerStorage.SENSITIVE.password.get()) || (
059                                                message.token != null && message.token.equals(device.getToken())))) {
060                                        device.rerollToken();
061                                        device.setClientConnection(connection);
062                                        connection.device = device;
063                                        LoginSuccessResponse response = new LoginSuccessResponse(message.requestId, device.id,
064                                                        device.getToken(), ServerStorage.MAIN.actions.size() - 1);
065                                        Playback.fillLoginSuccessResponse(response);
066                                        FilterListMessageHandler.fillLoginSuccessResponse(response);
067                                        connection.send(response);
068                                } else {
069                                        connection.send(
070                                                        new LoginFailResponse(message.requestId, LoginFailResponse.Reason.INCORRECT_CREDENTIALS));
071
072                                        // Make brute-forcing attacks less viable. This sleeps in the specific client's input thread, no
073                                        // other clients are impacted.
074                                        try {
075                                                Thread.sleep(2000);
076                                        } catch(InterruptedException e) {
077                                                Thread.currentThread().interrupt();
078                                        }
079                                }
080                                return;
081                        }
082                }
083
084                connection.send(new LoginFailResponse(message.requestId, LoginFailResponse.Reason.NO_SUCH_DEVICE));
085        }
086}