This fixes server crashing with people spamming BBS with packet editing due to memory leak[/COLOR]
Note : I'm using ystem.err.println(); , change it slfj-api if you want.
Replace your BBSOperationHandler with this :
package net.sf.odinms.net.channel.handler;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import net.sf.odinms.client.MapleCharacter;
import net.sf.odinms.client.MapleClient;
import net.sf.odinms.database.DatabaseConnection;
import net.sf.odinms.net.AbstractMaplePacketHandler;
import net.sf.odinms.tools.MaplePacketCreator;
import net.sf.odinms.tools.data.input.SeekableLittleEndianAccessor;
public class BBSOperationHandler extends AbstractMaplePacketHandler {
private String correctLength(String in, int maxSize) {
if (in.length() > maxSize)
return in.substring(0, maxSize);
return in;
}
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
if (c.getPlayer().getGuildId() <= 0) {
return; // expelled while viewing bbs or hax
}
int localthreadid = 0;
switch (slea.readByte()) {
case 0: // start a new post
boolean bEdit = slea.readByte() == 1 ? true : false;
if (bEdit) {
localthreadid = slea.readInt();
}
boolean bNotice = slea.readByte() == 1 ? true : false;
String title = correctLength(slea.readMapleAsciiString(), 25);
String text = correctLength(slea.readMapleAsciiString(), 600);
int icon = slea.readInt();
if (icon >= 0x64 && icon <= 0x6a) {
if (!c.getPlayer().haveItem(5290000 + icon - 0x64, 1, false, true))
return; // hax, using an nx icon that s/he doesn't have
} else if (!(icon >= 0 && icon <= 2))
return; // hax, using an invalid icon
if (!bEdit) {
newBBSThread(c, title, text, icon, bNotice);
} else {
editBBSThread(c, title, text, icon, localthreadid);
}
break;
case 1: // delete a thread
localthreadid = slea.readInt();
deleteBBSThread(c, localthreadid);
break;
case 2: // list threads
int start = slea.readInt();
listBBSThreads(c, start * 10);
break;
case 3: // list thread + reply, followed by id (int)
localthreadid = slea.readInt();
displayThread(c, localthreadid, true);
break;
case 4: // reply
localthreadid = slea.readInt();
text = correctLength(slea.readMapleAsciiString(), 25);
newBBSReply(c, localthreadid, text);
break;
case 5: // delete reply
localthreadid = slea.readInt(); // we don't use this
int replyid = slea.readInt();
deleteBBSReply(c, replyid);
break;
}
}
private static void listBBSThreads(MapleClient c, int start) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM bbs_threads WHERE guildid = ? ORDER BY localthreadid DESC");
ps.setInt(1, c.getPlayer().getGuildId());
ResultSet rs = ps.executeQuery();
if (rs.next()) {
c.getSession().write(MaplePacketCreator.BBSThreadList(rs, start));
} else {
c.getSession().write(MaplePacketCreator.BBS_noResult());
}
rs.close();
ps.close();
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void newBBSReply(MapleClient c, int localthreadid, String text) {
if (c.getPlayer().getGuildId() <= 0) {
return;
}
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT threadid FROM bbs_threads WHERE guildid = ? AND localthreadid = ?");
ps.setInt(1, c.getPlayer().getGuildId());
ps.setInt(2, localthreadid);
ResultSet threadRS = ps.executeQuery();
if (!threadRS.next()) {
threadRS.close();
ps.close();
return; // thread no longer exists, deleted?
}
int threadid = threadRS.getInt("threadid");
threadRS.close();
ps.close();
ps = con.prepareStatement("INSERT INTO bbs_replies (`threadid`, `postercid`, `timestamp`, `content`) VALUES " + "(?, ?, ?, ?)");
ps.setInt(1, threadid);
ps.setInt(2, c.getPlayer().getId());
ps.setLong(3, System.currentTimeMillis());
ps.setString(4, text);
ps.execute();
ps.close();
ps = con.prepareStatement("UPDATE bbs_threads SET replycount = replycount + 1 WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
displayThread(c, localthreadid, true);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void editBBSThread(MapleClient c, String title, String text, int icon, int localthreadid) {
if (c.getPlayer().getGuildId() <= 0) {
return; // expelled while viewing?
}
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE bbs_threads SET " + "`name` = ?, `timestamp` = ?, " + "`icon` = ?, " + "`startpost` = ? WHERE guildid = ? AND localthreadid = ? AND (postercid = ? OR ?)");
ps.setString(1, title);
ps.setLong(2, System.currentTimeMillis());
ps.setInt(3, icon);
ps.setString(4, text);
ps.setInt(5, c.getPlayer().getGuildId());
ps.setInt(6, localthreadid);
ps.setInt(7, c.getPlayer().getId());
ps.setBoolean(8, c.getPlayer().getGuildRank() <= 2);
ps.execute();
ps.close();
displayThread(c, localthreadid, true);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void newBBSThread(MapleClient c, String title, String text, int icon, boolean bNotice) {
if (c.getPlayer().getGuildId() <= 0) {
return; // expelled while viewing?
}
int nextId = 0;
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps;
if (!bNotice) { // notice's local id is always 0, so we don't need to fetch it
ps = con.prepareStatement("SELECT MAX(localthreadid) AS lastLocalId FROM bbs_threads WHERE guildid = ?");
ps.setInt(1, c.getPlayer().getGuildId());
ResultSet rs = ps.executeQuery();
rs.next();
nextId = rs.getInt("lastLocalId") + 1;
rs.close();
ps.close();
}
ps = con.prepareStatement("INSERT INTO bbs_threads (`postercid`, `name`, `timestamp`, `icon`, `startpost`, " + "`guildid`, `localthreadid`) VALUES(?, ?, ?, ?, ?, ?, ?)");
ps.setInt(1, c.getPlayer().getId());
ps.setString(2, title);
ps.setLong(3, System.currentTimeMillis());
ps.setInt(4, icon);
ps.setString(5, text);
ps.setInt(6, c.getPlayer().getGuildId());
ps.setInt(7, nextId);
ps.execute();
ps.close();
displayThread(c, nextId, true);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void deleteBBSThread(MapleClient c, int localthreadid) {
if (c.getPlayer().getGuildId() <= 0) {
return;
}
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT threadid, postercid FROM bbs_threads WHERE guildid = ? AND localthreadid = ?");
ps.setInt(1, c.getPlayer().getGuildId());
ps.setInt(2, localthreadid);
ResultSet threadRS = ps.executeQuery();
if (!threadRS.next()) {
threadRS.close();
ps.close();
return; // thread no longer exists, deleted?
}
if (c.getPlayer().getId() != threadRS.getInt("postercid") && c.getPlayer().getGuildRank() > 2) {
threadRS.close();
ps.close();
return; // [hax] deleting a thread that he didn't make
}
int threadid = threadRS.getInt("threadid");
threadRS.close();
ps.close();
ps = con.prepareStatement("DELETE FROM bbs_replies WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
ps = con.prepareStatement("DELETE FROM bbs_threads WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void deleteBBSReply(MapleClient c, int replyid) {
if (c.getPlayer().getGuildId() <= 0) {
return;
}
int threadid;
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT postercid, threadid FROM bbs_replies WHERE replyid = ?");
ps.setInt(1, replyid);
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
return; // thread no longer exists, deleted?
}
if (c.getPlayer().getId() != rs.getInt("postercid") && c.getPlayer().getGuildRank() > 2) {
rs.close();
ps.close();
return; // [hax] deleting a reply that he didn't make
}
threadid = rs.getInt("threadid");
rs.close();
ps.close();
ps = con.prepareStatement("DELETE FROM bbs_replies WHERE replyid = ?");
ps.setInt(1, replyid);
ps.execute();
ps.close();
ps = con.prepareStatement("UPDATE bbs_threads SET replycount = replycount - 1 WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
displayThread(c, threadid, false);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
Note : I'm using ystem.err.println(); , change it slfj-api if you want.
Replace your BBSOperationHandler with this :
package net.sf.odinms.net.channel.handler;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import net.sf.odinms.client.MapleCharacter;
import net.sf.odinms.client.MapleClient;
import net.sf.odinms.database.DatabaseConnection;
import net.sf.odinms.net.AbstractMaplePacketHandler;
import net.sf.odinms.tools.MaplePacketCreator;
import net.sf.odinms.tools.data.input.SeekableLittleEndianAccessor;
public class BBSOperationHandler extends AbstractMaplePacketHandler {
private String correctLength(String in, int maxSize) {
if (in.length() > maxSize)
return in.substring(0, maxSize);
return in;
}
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
if (c.getPlayer().getGuildId() <= 0) {
return; // expelled while viewing bbs or hax
}
int localthreadid = 0;
switch (slea.readByte()) {
case 0: // start a new post
boolean bEdit = slea.readByte() == 1 ? true : false;
if (bEdit) {
localthreadid = slea.readInt();
}
boolean bNotice = slea.readByte() == 1 ? true : false;
String title = correctLength(slea.readMapleAsciiString(), 25);
String text = correctLength(slea.readMapleAsciiString(), 600);
int icon = slea.readInt();
if (icon >= 0x64 && icon <= 0x6a) {
if (!c.getPlayer().haveItem(5290000 + icon - 0x64, 1, false, true))
return; // hax, using an nx icon that s/he doesn't have
} else if (!(icon >= 0 && icon <= 2))
return; // hax, using an invalid icon
if (!bEdit) {
newBBSThread(c, title, text, icon, bNotice);
} else {
editBBSThread(c, title, text, icon, localthreadid);
}
break;
case 1: // delete a thread
localthreadid = slea.readInt();
deleteBBSThread(c, localthreadid);
break;
case 2: // list threads
int start = slea.readInt();
listBBSThreads(c, start * 10);
break;
case 3: // list thread + reply, followed by id (int)
localthreadid = slea.readInt();
displayThread(c, localthreadid, true);
break;
case 4: // reply
localthreadid = slea.readInt();
text = correctLength(slea.readMapleAsciiString(), 25);
newBBSReply(c, localthreadid, text);
break;
case 5: // delete reply
localthreadid = slea.readInt(); // we don't use this
int replyid = slea.readInt();
deleteBBSReply(c, replyid);
break;
}
}
private static void listBBSThreads(MapleClient c, int start) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM bbs_threads WHERE guildid = ? ORDER BY localthreadid DESC");
ps.setInt(1, c.getPlayer().getGuildId());
ResultSet rs = ps.executeQuery();
if (rs.next()) {
c.getSession().write(MaplePacketCreator.BBSThreadList(rs, start));
} else {
c.getSession().write(MaplePacketCreator.BBS_noResult());
}
rs.close();
ps.close();
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void newBBSReply(MapleClient c, int localthreadid, String text) {
if (c.getPlayer().getGuildId() <= 0) {
return;
}
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT threadid FROM bbs_threads WHERE guildid = ? AND localthreadid = ?");
ps.setInt(1, c.getPlayer().getGuildId());
ps.setInt(2, localthreadid);
ResultSet threadRS = ps.executeQuery();
if (!threadRS.next()) {
threadRS.close();
ps.close();
return; // thread no longer exists, deleted?
}
int threadid = threadRS.getInt("threadid");
threadRS.close();
ps.close();
ps = con.prepareStatement("INSERT INTO bbs_replies (`threadid`, `postercid`, `timestamp`, `content`) VALUES " + "(?, ?, ?, ?)");
ps.setInt(1, threadid);
ps.setInt(2, c.getPlayer().getId());
ps.setLong(3, System.currentTimeMillis());
ps.setString(4, text);
ps.execute();
ps.close();
ps = con.prepareStatement("UPDATE bbs_threads SET replycount = replycount + 1 WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
displayThread(c, localthreadid, true);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void editBBSThread(MapleClient c, String title, String text, int icon, int localthreadid) {
if (c.getPlayer().getGuildId() <= 0) {
return; // expelled while viewing?
}
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE bbs_threads SET " + "`name` = ?, `timestamp` = ?, " + "`icon` = ?, " + "`startpost` = ? WHERE guildid = ? AND localthreadid = ? AND (postercid = ? OR ?)");
ps.setString(1, title);
ps.setLong(2, System.currentTimeMillis());
ps.setInt(3, icon);
ps.setString(4, text);
ps.setInt(5, c.getPlayer().getGuildId());
ps.setInt(6, localthreadid);
ps.setInt(7, c.getPlayer().getId());
ps.setBoolean(8, c.getPlayer().getGuildRank() <= 2);
ps.execute();
ps.close();
displayThread(c, localthreadid, true);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void newBBSThread(MapleClient c, String title, String text, int icon, boolean bNotice) {
if (c.getPlayer().getGuildId() <= 0) {
return; // expelled while viewing?
}
int nextId = 0;
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps;
if (!bNotice) { // notice's local id is always 0, so we don't need to fetch it
ps = con.prepareStatement("SELECT MAX(localthreadid) AS lastLocalId FROM bbs_threads WHERE guildid = ?");
ps.setInt(1, c.getPlayer().getGuildId());
ResultSet rs = ps.executeQuery();
rs.next();
nextId = rs.getInt("lastLocalId") + 1;
rs.close();
ps.close();
}
ps = con.prepareStatement("INSERT INTO bbs_threads (`postercid`, `name`, `timestamp`, `icon`, `startpost`, " + "`guildid`, `localthreadid`) VALUES(?, ?, ?, ?, ?, ?, ?)");
ps.setInt(1, c.getPlayer().getId());
ps.setString(2, title);
ps.setLong(3, System.currentTimeMillis());
ps.setInt(4, icon);
ps.setString(5, text);
ps.setInt(6, c.getPlayer().getGuildId());
ps.setInt(7, nextId);
ps.execute();
ps.close();
displayThread(c, nextId, true);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void deleteBBSThread(MapleClient c, int localthreadid) {
if (c.getPlayer().getGuildId() <= 0) {
return;
}
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT threadid, postercid FROM bbs_threads WHERE guildid = ? AND localthreadid = ?");
ps.setInt(1, c.getPlayer().getGuildId());
ps.setInt(2, localthreadid);
ResultSet threadRS = ps.executeQuery();
if (!threadRS.next()) {
threadRS.close();
ps.close();
return; // thread no longer exists, deleted?
}
if (c.getPlayer().getId() != threadRS.getInt("postercid") && c.getPlayer().getGuildRank() > 2) {
threadRS.close();
ps.close();
return; // [hax] deleting a thread that he didn't make
}
int threadid = threadRS.getInt("threadid");
threadRS.close();
ps.close();
ps = con.prepareStatement("DELETE FROM bbs_replies WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
ps = con.prepareStatement("DELETE FROM bbs_threads WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
private static void deleteBBSReply(MapleClient c, int replyid) {
if (c.getPlayer().getGuildId() <= 0) {
return;
}
int threadid;
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT postercid, threadid FROM bbs_replies WHERE replyid = ?");
ps.setInt(1, replyid);
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
return; // thread no longer exists, deleted?
}
if (c.getPlayer().getId() != rs.getInt("postercid") && c.getPlayer().getGuildRank() > 2) {
rs.close();
ps.close();
return; // [hax] deleting a reply that he didn't make
}
threadid = rs.getInt("threadid");
rs.close();
ps.close();
ps = con.prepareStatement("DELETE FROM bbs_replies WHERE replyid = ?");
ps.setInt(1, replyid);
ps.execute();
ps.close();
ps = con.prepareStatement("UPDATE bbs_threads SET replycount = replycount - 1 WHERE threadid = ?");
ps.setInt(1, threadid);
ps.execute();
ps.close();
displayThread(c, threadid, false);
} catch (SQLException se) {
System.err.println("SQLException: " + se.getLocalizedMessage() + se);
}
}
Last edited by Admin on Tue May 19, 2009 6:21 am; edited 1 time in total