/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.modules;
import static jpcsp.HLE.kernel.types.SceKernelThreadInfo.THREAD_CALLBACK_USER_DEFINED;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import javax.swing.JOptionPane;
import jpcsp.HLE.BufferInfo;
import jpcsp.HLE.BufferInfo.LengthInfo;
import jpcsp.HLE.BufferInfo.Usage;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer32;
import jpcsp.HLE.Modules;
import jpcsp.HLE.kernel.types.SceKernelCallbackInfo;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.kernel.types.SceNpAuthRequestParameter;
import jpcsp.HLE.kernel.types.SceNpTicket;
import jpcsp.HLE.kernel.types.SceNpTicket.TicketParam;
import jpcsp.memory.IMemoryReader;
import jpcsp.memory.IMemoryWriter;
import jpcsp.memory.MemoryReader;
import jpcsp.memory.MemoryWriter;
import jpcsp.util.Utilities;
import org.apache.log4j.Logger;
public class sceNpAuth extends HLEModule {
public static Logger log = Modules.getLogger("sceNpAuth");
public static boolean useDummyTicket = false;
public final static int STATUS_ACCOUNT_SUSPENDED = 0x80;
public final static int STATUS_ACCOUNT_CHAT_RESTRICTED = 0x100;
public final static int STATUS_ACCOUNT_PARENTAL_CONTROL_ENABLED = 0x200;
private boolean initialized;
private int npMemSize; // Memory allocated by the NP utility.
private int npMaxMemSize; // Maximum memory used by the NP utility.
private int npFreeMemSize; // Free memory available to use by the NP utility.
private SceKernelCallbackInfo npAuthCreateTicketCallback;
private String serviceId;
private byte[] ticketBytes = new byte[10000];
private int ticketBytesLength;
@Override
public void start() {
initialized = false;
super.start();
}
protected void checkInitialized() {
if (!initialized) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_NPAUTH_NOT_INIT);
}
}
public static void addTicketParam(SceNpTicket ticket, int type, String value, int length) {
byte[] stringBytes = value.getBytes(Charset.forName("ASCII"));
byte[] bytes = new byte[length];
System.arraycopy(stringBytes, 0, bytes, 0, Math.min(length, stringBytes.length));
ticket.parameters.add(new TicketParam(type, bytes));
}
public static void addTicketParam(SceNpTicket ticket, String value, int length) {
addTicketParam(ticket, TicketParam.PARAM_TYPE_STRING_ASCII, value, length);
}
public static void addTicketParam(SceNpTicket ticket, int value) {
byte bytes[] = new byte[4];
Utilities.writeUnaligned32(bytes, 0, Utilities.endianSwap32(value));
ticket.parameters.add(new TicketParam(TicketParam.PARAM_TYPE_INT, bytes));
}
public static void addTicketDateParam(SceNpTicket ticket, long time) {
byte bytes[] = new byte[8];
Utilities.writeUnaligned32(bytes, 0, Utilities.endianSwap32((int) (time >> 32)));
Utilities.writeUnaligned32(bytes, 4, Utilities.endianSwap32((int) time));
ticket.parameters.add(new TicketParam(TicketParam.PARAM_TYPE_DATE, bytes));
}
public static void addTicketLongParam(SceNpTicket ticket, long value) {
byte bytes[] = new byte[8];
Utilities.writeUnaligned32(bytes, 0, Utilities.endianSwap32((int) (value >> 32)));
Utilities.writeUnaligned32(bytes, 4, Utilities.endianSwap32((int) value));
ticket.parameters.add(new TicketParam(TicketParam.PARAM_TYPE_LONG, bytes));
}
public static void addTicketParam(SceNpTicket ticket) {
ticket.parameters.add(new TicketParam(TicketParam.PARAM_TYPE_NULL, new byte[0]));
}
private static String encodeURLParam(String value) {
try {
return URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException e) {
return value;
}
}
private static void addURLParam(StringBuilder params, String name, String value) {
if (params.length() > 0) {
params.append("&");
}
params.append(name);
params.append("=");
params.append(encodeURLParam(value));
}
private static void addURLParam(StringBuilder params, String name, int addr, int length) {
StringBuilder value = new StringBuilder();
IMemoryReader memoryReader = MemoryReader.getMemoryReader(addr, length, 1);
for (int i = 0; i < length; i++) {
int c = memoryReader.readNext();
value.append((char) c);
}
addURLParam(params, name, value.toString());
}
/**
* Initialization.
*
* @param poolSize
* @param stackSize
* @param threadPriority
* @return
*/
@HLEUnimplemented
@HLEFunction(nid = 0xA1DE86F8, version = 150, checkInsideInterrupt = true)
public int sceNpAuthInit(int poolSize, int stackSize, int threadPriority) {
npMemSize = poolSize;
npMaxMemSize = poolSize / 2; // Dummy
npFreeMemSize = poolSize - 16; // Dummy.
initialized = true;
return 0;
}
/**
* Termination.
*
* @return
*/
@HLEUnimplemented
@HLEFunction(nid = 0x4EC1F667, version = 150, checkInsideInterrupt = true)
public int sceNpAuthTerm() {
initialized = false;
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xF4531ADC, version = 150, checkInsideInterrupt = true)
public int sceNpAuthGetMemoryStat(TPointer32 memStatAddr) {
checkInitialized();
memStatAddr.setValue(0, npMemSize);
memStatAddr.setValue(4, npMaxMemSize);
memStatAddr.setValue(8, npFreeMemSize);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xCD86A656, version = 150)
public int sceNpAuthCreateStartRequest(TPointer paramAddr) {
SceNpAuthRequestParameter param = new SceNpAuthRequestParameter();
param.read(paramAddr);
if (log.isInfoEnabled()) {
log.info(String.format("sceNpAuthCreateStartRequest param: %s", param));
}
serviceId = param.serviceId;
if (!useDummyTicket) {
String loginId = JOptionPane.showInputDialog("Enter your PSN Sign-In ID (Email Address)");
if (loginId != null) {
String password = JOptionPane.showInputDialog("Enter your PSN Password");
if (password != null) {
StringBuilder params = new StringBuilder();
addURLParam(params, "serviceid", serviceId);
addURLParam(params, "loginid", loginId);
addURLParam(params, "password", password);
if (param.cookie != 0) {
addURLParam(params, "cookie", param.cookie, param.cookieSize);
}
if (param.entitlementIdAddr != 0) {
addURLParam(params, "entitlementid", param.entitlementId);
addURLParam(params, "consumedcount", Integer.toString(param.consumedCount));
}
HttpURLConnection connection = null;
ticketBytesLength = 0;
try {
connection = (HttpURLConnection) new URL("https://auth.np.ac.playstation.net/nav/auth").openConnection();
connection.setRequestProperty("X-I-5-Version", "2.1");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("X-Platform-Version", "PSP 06.60");
connection.setRequestProperty("Content-Length", Integer.toString(params.length()));
connection.setRequestProperty("User-Agent", "Lediatio Lunto Ritna");
connection.setRequestMethod("POST");
connection.setDoOutput(true);
OutputStream os = connection.getOutputStream();
os.write(params.toString().getBytes());
os.close();
connection.connect();
int responseCode = connection.getResponseCode();
if (log.isDebugEnabled()) {
log.debug(String.format("Response code: %d", responseCode));
for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
log.debug(String.format("%s: %s", entry.getKey(), entry.getValue()));
}
}
if (responseCode == 200) {
InputStream in = connection.getInputStream();
while (true) {
int length = in.read(ticketBytes, ticketBytesLength, ticketBytes.length - ticketBytesLength);
if (length < 0) {
break;
}
ticketBytesLength += length;
}
in.close();
if (log.isDebugEnabled()) {
log.debug(String.format("Received ticket: %s", Utilities.getMemoryDump(ticketBytes, 0, ticketBytesLength)));
}
}
} catch (MalformedURLException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
}
if (param.ticketCallback != 0) {
int ticketLength = ticketBytesLength > 0 ? ticketBytesLength : 248;
npAuthCreateTicketCallback = Modules.ThreadManForUserModule.hleKernelCreateCallback("sceNpAuthCreateStartRequest", param.ticketCallback, param.callbackArgument);
if (Modules.ThreadManForUserModule.hleKernelRegisterCallback(THREAD_CALLBACK_USER_DEFINED, npAuthCreateTicketCallback.getUid())) {
Modules.ThreadManForUserModule.hleKernelNotifyCallback(THREAD_CALLBACK_USER_DEFINED, npAuthCreateTicketCallback.getUid(), ticketLength);
}
}
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x3F1C1F70, version = 150)
public int sceNpAuthGetTicket(int id, TPointer buffer, int length) {
int result;
if (useDummyTicket) {
SceNpTicket ticket = new SceNpTicket();
ticket.version = 0x00000121;
ticket.size = 0xF0;
addTicketParam(ticket, "XXXXXXXXXXXXXXXXXXXX", 20);
addTicketParam(ticket, 0);
long now = System.currentTimeMillis();
addTicketDateParam(ticket, now);
addTicketDateParam(ticket, now + 10 * 60 * 1000); // now + 10 minutes
addTicketLongParam(ticket, 0L);
addTicketParam(ticket, TicketParam.PARAM_TYPE_STRING, "DummyOnlineID", 32);
addTicketParam(ticket, "gb", 4);
addTicketParam(ticket, TicketParam.PARAM_TYPE_STRING, "XX", 4);
addTicketParam(ticket, serviceId, 24);
int status = 0;
if (Modules.sceNpModule.parentalControl == sceNp.PARENTAL_CONTROL_ENABLED) {
status |= STATUS_ACCOUNT_PARENTAL_CONTROL_ENABLED;
}
status |= (Modules.sceNpModule.getUserAge() & 0x7F) << 24;
addTicketParam(ticket, status);
addTicketParam(ticket);
addTicketParam(ticket);
ticket.unknownBytes = new byte[72];
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpAuthGetTicket returning dummy ticket: %s", ticket));
}
ticket.write(buffer);
result = ticket.sizeof();
} else if (ticketBytesLength > 0) {
result = Math.min(ticketBytesLength, length);
IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(buffer.getAddress(), result, 1);
for (int i = 0; i < result; i++) {
memoryWriter.writeNext(ticketBytes[i] & 0xFF);
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpAuthGetTicket returning real ticket: %s", Utilities.getMemoryDump(buffer.getAddress(), result)));
}
} else {
buffer.clear(length);
result = length;
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpAuthGetTicket returning empty ticket"));
}
}
return result;
}
@HLEUnimplemented
@HLEFunction(nid = 0x6900F084, version = 150)
public int sceNpAuthGetEntitlementById(TPointer ticketBuffer, int ticketLength, int unknown1, int unknown2) {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x72BB0467, version = 150)
public int sceNpAuthDestroyRequest(int id) {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xD99455DD, version = 150)
public int sceNpAuthAbortRequest(int id) {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x5A3CB57A, version = 150)
public int sceNpAuthGetTicketParam(@BufferInfo(lengthInfo=LengthInfo.nextParameter, usage=Usage.in) TPointer ticketBuffer, int ticketLength, int paramNumber, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=256, usage=Usage.out) TPointer buffer) {
// This clear is always done, even when an error is returned
buffer.clear(256);
if (paramNumber < 0 || paramNumber >= SceNpTicket.NUMBER_PARAMETERS) {
return SceKernelErrors.ERROR_NP_MANAGER_INVALID_ARGUMENT;
}
if (ticketBuffer.getValue32() == 0) {
// This is an empty ticket, do no analyze it
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpAuthGetTicketParam returning empty param from empty ticket"));
}
} else {
SceNpTicket ticket = new SceNpTicket();
ticket.read(ticketBuffer);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpAuthGetTicketParam ticket: %s", ticket));
}
TicketParam ticketParam = ticket.parameters.get(paramNumber);
ticketParam.writeForPSP(buffer);
}
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x75FB0AE3, version = 150)
public int sceNpAuthGetEntitlementIdList() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x61BB18B3, version = 150)
public int sceNpAuth_61BB18B3() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xB714FBDD, version = 150)
public int sceNpAuth_B714FBDD() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xCE85B3B8, version = 150)
public int sceNpAuth_CE85B3B8() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xDAD65284, version = 150)
public int sceNpAuth_DAD65284() {
return 0;
}
}