edu.tsinghua.lumaqq.qq.packets.Packet.java Source code

Java tutorial

Introduction

Here is the source code for edu.tsinghua.lumaqq.qq.packets.Packet.java

Source

/*
* 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;
    }
}