Fix MFC websocket message parser
This commit is contained in:
parent
2126b61e99
commit
0e61421537
|
@ -40,6 +40,7 @@ import com.squareup.moshi.Moshi;
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.StringUtil;
|
import ctbrec.StringUtil;
|
||||||
|
import ctbrec.io.HttpException;
|
||||||
import okhttp3.Cookie;
|
import okhttp3.Cookie;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
@ -93,6 +94,7 @@ public class MyFreeCamsClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() throws IOException {
|
public void start() throws IOException {
|
||||||
|
requestLandingPage(); // to get some cookies
|
||||||
running = true;
|
running = true;
|
||||||
serverConfig = new ServerConfig(mfc);
|
serverConfig = new ServerConfig(mfc);
|
||||||
List<String> websocketServers = new ArrayList<>(serverConfig.wsServers.size());
|
List<String> websocketServers = new ArrayList<>(serverConfig.wsServers.size());
|
||||||
|
@ -134,6 +136,21 @@ public class MyFreeCamsClient {
|
||||||
watchDog.start();
|
watchDog.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requestLandingPage() throws IOException {
|
||||||
|
Request req = new Request.Builder()
|
||||||
|
.url(MyFreeCams.baseUrl)
|
||||||
|
.header(ACCEPT, "*/*")
|
||||||
|
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
||||||
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
|
.header(CONNECTION, KEEP_ALIVE)
|
||||||
|
.build();
|
||||||
|
try(Response resp = mfc.getHttpClient().execute(req)) {
|
||||||
|
if(!resp.isSuccessful()) {
|
||||||
|
throw new HttpException(resp.code(), resp.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
running = false;
|
running = false;
|
||||||
ws.close(1000, "Good Bye"); // terminate normally (1000)
|
ws.close(1000, "Good Bye"); // terminate normally (1000)
|
||||||
|
@ -455,54 +472,6 @@ public class MyFreeCamsClient {
|
||||||
model.update(state, getStreamUrl(state));
|
model.update(state, getStreamUrl(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message parseMessage(StringBuilder msgBuffer) throws UnsupportedEncodingException {
|
|
||||||
int packetLengthBytes = 6;
|
|
||||||
if (msgBuffer.length() < packetLengthBytes) {
|
|
||||||
// packet size not transmitted completely
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
int packetLength = Integer.parseInt(msgBuffer.substring(0, packetLengthBytes));
|
|
||||||
if (packetLength > msgBuffer.length() - packetLengthBytes) {
|
|
||||||
// packet not complete
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
LOG.trace("<-- {}", msgBuffer);
|
|
||||||
msgBuffer.delete(0, packetLengthBytes);
|
|
||||||
StringBuilder rawMessage = new StringBuilder(msgBuffer.substring(0, packetLength));
|
|
||||||
int type = parseNextInt(rawMessage);
|
|
||||||
int sender = parseNextInt(rawMessage);
|
|
||||||
int receiver = parseNextInt(rawMessage);
|
|
||||||
int arg1 = parseNextInt(rawMessage);
|
|
||||||
int arg2 = parseNextInt(rawMessage);
|
|
||||||
Message message = new Message(type, sender, receiver, arg1, arg2, URLDecoder.decode(rawMessage.toString(), "utf-8"));
|
|
||||||
msgBuffer.delete(0, packetLength);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.error("StringBuilder contains invalid data {}", msgBuffer.toString(), e);
|
|
||||||
String logfile = "mfc_messages.log";
|
|
||||||
try (FileOutputStream fout = new FileOutputStream(logfile)) {
|
|
||||||
for (String string : receivedTextHistory) {
|
|
||||||
fout.write(string.getBytes());
|
|
||||||
fout.write(10);
|
|
||||||
}
|
|
||||||
} catch (Exception e1) {
|
|
||||||
LOG.error("Couldn't write mfc message history to {}", logfile, e1);
|
|
||||||
}
|
|
||||||
msgBuffer.setLength(0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int parseNextInt(StringBuilder s) {
|
|
||||||
int nextSpace = s.indexOf(" ");
|
|
||||||
int i = Integer.parseInt(s.substring(0, nextSpace));
|
|
||||||
s.delete(0, nextSpace + 1);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
||||||
super.onMessage(webSocket, bytes);
|
super.onMessage(webSocket, bytes);
|
||||||
|
@ -512,6 +481,58 @@ public class MyFreeCamsClient {
|
||||||
return websocket;
|
return websocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Message parseMessage(StringBuilder msgBuffer) throws UnsupportedEncodingException {
|
||||||
|
int packetLengthBytes = parsePacketLengthBytes(msgBuffer);
|
||||||
|
if (packetLengthBytes < 0) {
|
||||||
|
// packet size not transmitted completely
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
int packetLength = Integer.parseInt(msgBuffer.substring(0, packetLengthBytes));
|
||||||
|
if (packetLength > msgBuffer.length() - packetLengthBytes) {
|
||||||
|
// packet not complete
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
LOG.trace("<-- {}", msgBuffer);
|
||||||
|
msgBuffer.delete(0, packetLengthBytes);
|
||||||
|
StringBuilder rawMessage = new StringBuilder(msgBuffer.substring(0, packetLength));
|
||||||
|
int type = parseNextInt(rawMessage);
|
||||||
|
int sender = parseNextInt(rawMessage);
|
||||||
|
int receiver = parseNextInt(rawMessage);
|
||||||
|
int arg1 = parseNextInt(rawMessage);
|
||||||
|
int arg2 = parseNextInt(rawMessage);
|
||||||
|
Message message = new Message(type, sender, receiver, arg1, arg2, URLDecoder.decode(rawMessage.toString(), "utf-8"));
|
||||||
|
msgBuffer.delete(0, packetLength);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("StringBuilder contains invalid data {}", msgBuffer.toString(), e);
|
||||||
|
String logfile = "mfc_messages.log";
|
||||||
|
try (FileOutputStream fout = new FileOutputStream(logfile)) {
|
||||||
|
for (String string : receivedTextHistory) {
|
||||||
|
fout.write(string.getBytes());
|
||||||
|
fout.write(10);
|
||||||
|
}
|
||||||
|
} catch (Exception e1) {
|
||||||
|
LOG.error("Couldn't write mfc message history to {}", logfile, e1);
|
||||||
|
}
|
||||||
|
msgBuffer.setLength(0);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parsePacketLengthBytes(StringBuilder msgBuffer) {
|
||||||
|
return msgBuffer.indexOf(" ") - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parseNextInt(StringBuilder s) {
|
||||||
|
int nextSpace = s.indexOf(" ");
|
||||||
|
int i = Integer.parseInt(s.substring(0, nextSpace));
|
||||||
|
s.delete(0, nextSpace + 1);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean follow(int uid) {
|
protected boolean follow(int uid) {
|
||||||
if (ws != null) {
|
if (ws != null) {
|
||||||
return ws.send(ADDFRIENDREQ + " " + sessionId + " 0 " + uid + " 1\n");
|
return ws.send(ADDFRIENDREQ + " " + sessionId + " 0 " + uid + " 1\n");
|
||||||
|
@ -703,4 +724,18 @@ public class MyFreeCamsClient {
|
||||||
public Collection<SessionState> getSessionStates() {
|
public Collection<SessionState> getSessionStates() {
|
||||||
return Collections.unmodifiableCollection(sessionStates.asMap().values());
|
return Collections.unmodifiableCollection(sessionStates.asMap().values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void joinChannel(MyFreeCamsModel model) {
|
||||||
|
SessionState state = getSessionState(model);
|
||||||
|
int userChannel = 100000000 + state.getUid();
|
||||||
|
LOG.debug("Joining chat channel for model {}", model.getDisplayName());
|
||||||
|
try {
|
||||||
|
search(model.getName());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
ws.send(MessageTypes.ROOMDATA + " " + sessionId + " 0 1 0\n");
|
||||||
|
ws.send(MessageTypes.UEOPT + " " + sessionId + " 0 66 1 111111\n");
|
||||||
|
ws.send(MessageTypes.JOINCHAN + " " + sessionId + " 0 " + userChannel + " 9\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package ctbrec;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ReflectionUtil {
|
||||||
|
|
||||||
|
private ReflectionUtil () {}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T call(Object target, String methodName, Object...args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||||
|
List<Class<?>> argTypes = Arrays.stream(args).map(arg -> arg.getClass()).collect(Collectors.toList());
|
||||||
|
Class<?>[] argTypeArray = argTypes.toArray(new Class[0]);
|
||||||
|
Method method = target.getClass().getDeclaredMethod(methodName, argTypeArray);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return (T) method.invoke(target, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package ctbrec.sites.mfc;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ctbrec.ReflectionUtil;
|
||||||
|
|
||||||
|
public class MyFreeCamsClientTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMessageParsing() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||||
|
MyFreeCamsClient client = MyFreeCamsClient.getInstance();
|
||||||
|
StringBuilder input = new StringBuilder("082720 418550763 419337943 0 37761695 %7B%22lv%22%3A4%2C%22nm%22%3A%22AliceArras%22%2C%22pid%22%3A1%2C%22sid%22%3A418550763%2C%22uid%22%3A37761695%2C%22vs%22%3A0%2C%22u%22%3A%7B%22age%22%3A19%2C%22avatar%22%3A1%2C%22blurb%22%3A%22Open%20minded%2C%20willing%20to%20try%20new%20horny%20things%20all%20the%20time!%22%2C%22camserv%22%3A1205%2C%22chat_font%22%3A0%2C%22chat_opt%22%3A1%2C%22city%22%3A%22Tallinn%22%2C%22country%22%3A%22Estonia%22%2C%22creation%22%3A1596715285%2C%22ethnic%22%3A%22Caucasian%22%2C%22photos%22%3A1%2C%22profile%22%3A1%2C%22status%22%3A%22%22%7D%2C%22m%22%3A%7B%22camscore%22%3A629.800%2C%22continent%22%3A%22EU%22%2C%22flags%22%3A3104%2C%22kbit%22%3A0%2C%22lastnews%22%3A0%2C%22mg%22%3A0%2C%22missmfc%22%3A0%2C%22new_model%22%3A1%2C%22rank%22%3A0%2C%22rc%22%3A4%2C%22sfw%22%3A0%2C%22topic%22%3A%22%22%7D%7D");
|
||||||
|
Message msg = ReflectionUtil.call(client, "parseMessage", input);
|
||||||
|
assertEquals(20, msg.getType());
|
||||||
|
assertEquals(418550763, msg.getSender());
|
||||||
|
assertEquals(419337943, msg.getReceiver());
|
||||||
|
assertEquals(0, msg.getArg1());
|
||||||
|
assertEquals(37761695, msg.getArg2());
|
||||||
|
assertEquals(JSONObject.class, new JSONObject(msg.getMessage()).getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMessageLengthParsing() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||||
|
MyFreeCamsClient client = MyFreeCamsClient.getInstance();
|
||||||
|
StringBuilder input = new StringBuilder("082720 418550763 419337943 0 37761695");
|
||||||
|
int packetBytesLenth = ReflectionUtil.call(client, "parsePacketLengthBytes", input);
|
||||||
|
assertEquals(4, packetBytesLenth);
|
||||||
|
|
||||||
|
input = new StringBuilder("82720 418550763 419337943 0 37761695");
|
||||||
|
packetBytesLenth = ReflectionUtil.call(client, "parsePacketLengthBytes", input);
|
||||||
|
assertEquals(3, packetBytesLenth);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue