Java tutorial
/* * LumaQQ - Java QQ Client * * Copyright (C) 2004 notXX * luma <stubma@163.com> * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.tsinghua.lumaqq.qq.packets; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.tsinghua.lumaqq.qq.Crypter; import edu.tsinghua.lumaqq.qq.QQ; import edu.tsinghua.lumaqq.qq.beans.QQUser; import edu.tsinghua.lumaqq.qq.debug.DebugSwitch; import edu.tsinghua.lumaqq.qq.debug.IDebugObject; import edu.tsinghua.lumaqq.qq.debug.PacketDO; /** * QQ * * @author notxx * @author luma */ public abstract class Packet { /** * logger. */ protected static Log log = LogFactory.getLog(Packet.class); /** * . */ protected static final Crypter crypter = new Crypter(); /** * back array??putBody * ?clear() */ protected static final ByteBuffer bodyBuf = ByteBuffer.allocate(QQ.QQ_MAX_PACKET_SIZE); /** ? */ protected static DebugSwitch ds = DebugSwitch.getInstance(); /** */ protected byte[] decryptKey; /** ??? */ protected byte[] fallbackDecryptKey; /** */ protected byte[] encryptKey; /** * , 0x03~0x04. */ protected char command; /** * ?, 0x01~0x02. */ protected char source; /** * ??, 0x05~0x06. */ protected char sequence; /** */ protected byte header; /** * QQUser * ?JVMQQClient???QQUser * */ protected QQUser user; /** * true????????LumaQQ? * ??????? * ???? */ protected boolean duplicated; /** */ protected byte[] bodyDecrypted; /** * ? * * @param header * * @param source * ? * @param command * * @param sequence * ?? * @param user * QQ */ public Packet(byte header, char source, char command, char sequence, QQUser user) { this.user = user; this.source = source; this.command = command; this.sequence = sequence; this.duplicated = false; this.header = header; } /** * bufOutPacketbuf????? * * @param buf * ByteBuffer * @throws PacketParseException * ? */ protected Packet(ByteBuffer buf, QQUser user) throws PacketParseException { this(buf, buf.limit() - buf.position(), user); } /** * bufOutPacketbuf????? * * @param buf * ByteBuffer * @param length * ?? * @throws PacketParseException * ? */ protected Packet(ByteBuffer buf, int length, QQUser user) throws PacketParseException { this.user = user; // ? parseHeader(buf); // QQ? if (!validateHeader()) throw new PacketParseException(": " + toString()); // byte[] body = getBodyBytes(buf, length); bodyDecrypted = decryptBody(body); if (bodyDecrypted == null) throw new PacketParseException("?: " + toString()); // ByteBuffer ByteBuffer tempBuf = ByteBuffer.wrap(bodyDecrypted); try { // ? parseBody(tempBuf); } catch (BufferUnderflowException e) { throw new PacketParseException(e.getMessage()); } parseTail(buf); // ??? if (ds.isDebug()) { byte[] debugContent = dump(); IDebugObject obj = new PacketDO(getPacketName(), debugContent, this instanceof InPacket, getHeadLength(), debugContent.length - getTailLength()); ds.deliverDebugObject(obj); } } /** * ? * * @return * */ public byte[] dump() { if (bodyDecrypted == null) return new byte[0]; else { byte[] debugContent = new byte[getLength(bodyDecrypted.length)]; ByteBuffer debugBuf = ByteBuffer.wrap(debugContent); putHead(debugBuf); debugBuf.put(bodyDecrypted); putTail(debugBuf); debugBuf = null; return debugContent; } } /** * ??? */ protected Packet() { } /** * UDP??TCP? * * @param bodyLength * * @return * */ protected abstract int getLength(int bodyLength); /** * * * @return * true */ protected abstract boolean validateHeader(); /** * @return * */ protected abstract int getHeadLength(); /** * @return * */ protected abstract int getTailLength(); /** * ?, ByteBuffer. * * @param buf * ByteBuffer. */ protected abstract void putHead(ByteBuffer buf); /** * ? * * @param buf * ByteBuffer */ protected abstract void putBody(ByteBuffer buf); /** * * * @param buf * ByteBuffer * @param length * * @return * */ protected abstract byte[] getBodyBytes(ByteBuffer buf, int length); /** * @return * ??? */ public abstract int getFamily(); /** * ?, ByteBuffer. * * @param buf * ByteBuffer. */ protected abstract void putTail(ByteBuffer buf); /** * * * @param b * * @return * */ protected byte[] encryptBody(byte[] b) { // get start int start = getEncryptStart(); if (start == -1) return b; // get length int length = getEncryptLength(); if (length == -1) length = b.length - start; // encrypt byte[] enc = crypter.encrypt(b, start, length, getEncryptKey(b)); // ? if (enc == null) return b; // byte[] ret = enc; if (b.length - length > 0) { ret = new byte[b.length - length + enc.length]; System.arraycopy(b, 0, ret, 0, start); System.arraycopy(enc, 0, ret, start, enc.length); System.arraycopy(b, start + length, ret, start + enc.length, b.length - start - length); } return ret; } /** * * * @param body * * @return */ protected byte[] decryptBody(byte[] body) { // int start = getDecryptStart(); if (start == -1) // return body; // int length = getDecryptLength(); if (length == -1) length = body.length - start; // byte[] dec = crypter.decrypt(body, start, length, getDecryptKey(body)); // if (dec == null) dec = crypter.decrypt(body, start, length, getFallbackDecryptKey(body)); // ? if (dec == null) return null; // start0 byte[] ret = dec; if (body.length - length > 0) { ret = new byte[dec.length + body.length - length]; System.arraycopy(body, 0, ret, 0, start); System.arraycopy(dec, 0, ret, start, dec.length); System.arraycopy(body, start + length, ret, start + dec.length, body.length - start - length); } return ret; } /** * @return * ???-1?? */ protected int getEncryptStart() { return 0; } /** * @return * ?-1? */ protected int getEncryptLength() { return -1; } /** * @return * ???-1 */ protected int getDecryptStart() { return 0; } /** * @return * -1? */ protected int getDecryptLength() { return -1; } /** * ?buf?? * * @param buf * ByteBuffer * @throws PacketParseException * ? */ protected abstract void parseBody(ByteBuffer buf) throws PacketParseException; /** * buf??? * * @param buf * ByteBuffer * @throws PacketParseException * ? */ protected abstract void parseHeader(ByteBuffer buf) throws PacketParseException; /** * buf?? * * @param buf * ByteBuffer * @throws PacketParseException * ? */ protected abstract void parseTail(ByteBuffer buf) throws PacketParseException; @Override public String toString() { return "??: " + getPacketName() + " ??: " + (int) sequence; } public String toDebugString() { return "toDebugString not implemented!"; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj instanceof Packet) { Packet packet = (Packet) obj; return header == packet.header && command == packet.command && sequence == packet.sequence; } else return super.equals(obj); } /** * ????. ????header?Header?? */ @Override public int hashCode() { return hash(sequence, command); } /** * hash * * @param header * @param sequence * @param command * @return */ public static int hash(char sequence, char command) { return (sequence << 16) | command; } /** * @return Returns the command. */ public char getCommand() { return command; } /** * @return Returns the sequence. */ public char getSequence() { return sequence; } /** * @param sequence The sequence to set. */ public void setSequence(char sequence) { this.sequence = sequence; } /** * @return * ???? */ public String getPacketName() { return "Unknown Packet"; } /** * @return Returns the source. */ public char getSource() { return source; } /** * @return Returns the duplicated. */ public boolean isDuplicated() { return duplicated; } /** * @param duplicated The duplicated to set. */ public void setDuplicated(boolean duplicated) { this.duplicated = duplicated; } /** * @return Returns the header. */ public byte getHeader() { return header; } /** * @param header The header to set. */ public void setHeader(byte header) { this.header = header; } public byte[] getDecryptKey(byte[] body) { return decryptKey; } public void setDecryptKey(byte[] decryptKey) { this.decryptKey = decryptKey; } public byte[] getEncryptKey(byte[] body) { return encryptKey; } public void setEncryptKey(byte[] encryptKey) { this.encryptKey = encryptKey; } public byte[] getFallbackDecryptKey(byte[] body) { return fallbackDecryptKey; } public void setFallbackDecryptKey(byte[] fallbackDecryptKey) { this.fallbackDecryptKey = fallbackDecryptKey; } }