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}