⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 normalim.java

📁 java写的qq代码实现qq的部分功能
💻 JAVA
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 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.beans;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;

import edu.tsinghua.lumaqq.qq.QQ;
import edu.tsinghua.lumaqq.qq.Util;


/**
 * <pre>
 * 普通消息的本体,其在NormalIMHeader之后
 * 
 * 普通消息中可能内嵌一些图片信息,除了普通的文本之外,图片的信息格式为:
 * 一. 缺省表情,缺省表情的前导字节是0x14,0x14之后的一个字节表示缺省表情的索引值
 * 二. 自定义表情,自定义表情的前导字节是0x15,0x15之后的格式为:
 * 	  1. 存在性字节,如果这个表情第一次出现,则为0x33,如果已经出现过,则为0x34,当为0x33时,后面的内容是
 * 		 i.   未知的一个字节
 *       ii.  表情图片的文件名,其文件名由md5的字符串形式和扩展名构成,因此这个长度应该是32 + 1 + 3(一般是GIF)
 *       iii. 表情的shortcut长度,以'A'为基准,如果长度是2,则这个字节是'C'
 *       iv.  表情的shortcut
 *    2. 如果为0x34时,则后面的内容为:
 * 		 i.   1字节索引值,假如这个自定义表情出现在第一个位置,则这个字节为'A'   
 *    3. 如果为0x36时
 * 		 i. 自定义表情协议块的长度的10进制字符串形式,3字节,不足者前部填为空格,比如为了表示这个自定义表情用了
 *          88个字节,那么这个字段就是" 88",呵呵,晕吧,注意这个长度是从0x15开始算起,一直到结束。注意这个长度
 *          是字节长度,所以如果有中文在里面,这个字段靠不住,这个给我们带来了一些麻烦
 *       ii. 表情标识,1字节,标识这个表情是新的,还是已经出现过的,如果是新的,用'e'表示。如果是已经出现过的,
 *           用一个大写字母表示,第一个新表情代号是A,第二个是B,以此类推
 *       iii. 表情的快捷键字节长度,1字节,用一个大写字母表示,比如A表示长度为0,一次类推
 *       iv. 后面的内容开始一直到agent key之前的内容的长度,2字节,用16进制的字符串表示
 *       v. session id的16进制字符串形式,8字节,不足者前面是空格
 *       vi. 中转服务器IP的16进制字符串形式,注意是little-endian,那么ipv4的话自然就是8个字节了
 *       vii. 中转服务器端口号的16进制字符串形式,8个字节
 *       viii. file agent key,16字节
 *       ix. 图片的文件名,文件名的形式是MD5的字符串形式加上点加上后缀名而成,所以一般是36个字节,但是
 *           我想最好还是根据前面的长度减去其他字段的长度来判断好些
 *       x.  快捷键,长度前面已经说了
 *       xi. 一个字节,'A',可能是用来分界用的
 *    4. 如果为0x37时
 *       0x37表示这个表情已经在前面出现过,参见0x36时的格式,0x37缺少0x36的iv, v, vii, viii, ix部分,
 *       其他部分均相同
 * </pre>
 * 
 * @author 马若劼
 */
public class NormalIM {
    public boolean hasFontAttribute; // 是一个字节,为了方便,我弄成boolean表示
    public int totalFragments;
    public int fragmentSequence;
    public char messageId;
    public byte replyType;
    // 下面这些都是消息的属性,比如字体啦颜色啦,都是在fontAttribute里面的
    public String encoding;
    public int red, green, blue;
    public int fontSize;
    public String fontName;
    public boolean bold, italic, underline;
    
	// 消息内容,在解析的时候只用byte[],正式要显示到界面上时才会转为String,上层程序
	// 要负责这个事,这个类只负责把内容读入byte[]
	public byte[] messageBytes;
	public String message;
    
    /**
     * 给定一个输入流,解析NormalIM结构
     * @param buf
     */
    public void readBean(ByteBuffer buf) {
        // 是否有字体属性
        hasFontAttribute = buf.getInt() != 0;
        // 分片数
        totalFragments = buf.get() & 0xFF;
        // 分片序号
        fragmentSequence = buf.get() & 0xFF;
        // 消息id
        messageId = buf.getChar();
        // 消息类型,这里的类型表示是正常回复还是自动回复之类的信息
        replyType = buf.get();
        // 消息正文,0是分隔符
        // 先不停的读,直到找到0x0为止
        // 要注意的是Gaim 0.64的QQ插件在这里不以0结尾,对于这种情况,我们需要处理
        // 不然Gaim 0.64发来的消息会解析错误
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while(buf.hasRemaining()) {
            byte b = buf.get();
            if(b == 0) 
            	break;
            else if(b == QQ.QQ_CUSTOM_FACE_TAG) {
                baos.write(QQ.QQ_CUSTOM_FACE_TAG);
                baos.write(0x30);
                
                /* 在这里,过滤自定义表情,因为目前还不支持 */
                byte existence = buf.get();
                if(existence == QQ.QQ_CUSTOM_FACE_EXIST)
                    buf.get();
                else if(existence == QQ.QQ_CUSTOM_FACE_NEW) {
                    buf.position(buf.position() + 37);
                    int shortcutLen = (int)(buf.get() & 0xFF) - 'A';
                    buf.position(buf.position() + shortcutLen);
                } 
            } else
            	baos.write(b);
        }
        messageBytes = baos.toByteArray();
        // 这后面都是字体属性,这个和SendIMPacket里面的是一样的,我想大概服务器就是直接拷贝那一块
        if(hasFontAttribute) {
            if(buf.hasRemaining()) {
    	        byte fontStyle = buf.get();
    	        // 分析字体属性到具体的变量
    	        // 字体大小
    	        fontSize = fontStyle & 0x1F;
    	        // 组体,斜体,下画线
    	        bold = (fontStyle & 0x20) != 0;
    	        italic = (fontStyle & 0x40) != 0;
    	        underline = (fontStyle & 0x80) != 0;
    	        // 字体颜色rgb
    	        red = buf.get() & 0xFF;
    	        green = buf.get() & 0xFF;
    	        blue = buf.get() & 0xFF;
    	        // 1个未知字节
    	        buf.get();
    	        // 消息编码,这个据Gaim QQ的注释,这个字段用处不大,说是如果在一个英文windows
    	        // 里面输入了中文,那么编码是英文的,按照这个encoding来解码就不行了
    	        // 不过我们还是得到这个字段吧,后面我们采用先缺省GBK解码,不行就这个encoding
    	        // 解码,再不行就ISO-8859-1的方式
    	        encoding = Util.getEncodingString(buf.getChar());
    	        // 字体名称,字体名称也有中文的也有英文的,所以。。先来试试缺省的
    	        fontName = Util.getString(buf, buf.remaining() - 1);                
            } else
                hasFontAttribute = false;
        } 
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -