📄 receiveimpacket.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.packets.in;
import static edu.tsinghua.lumaqq.qq.events.QQEvent.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import edu.tsinghua.lumaqq.qq.QQ;
import edu.tsinghua.lumaqq.qq.Util;
import edu.tsinghua.lumaqq.qq.annotation.DocumentalPacket;
import edu.tsinghua.lumaqq.qq.annotation.LinkedEvent;
import edu.tsinghua.lumaqq.qq.annotation.PacketName;
import edu.tsinghua.lumaqq.qq.annotation.RelatedPacket;
import edu.tsinghua.lumaqq.qq.beans.ClusterIM;
import edu.tsinghua.lumaqq.qq.beans.CustomHead;
import edu.tsinghua.lumaqq.qq.beans.FileInfo;
import edu.tsinghua.lumaqq.qq.beans.FileTransferArgs;
import edu.tsinghua.lumaqq.qq.beans.NormalIM;
import edu.tsinghua.lumaqq.qq.beans.NormalIMHeader;
import edu.tsinghua.lumaqq.qq.beans.QQLive;
import edu.tsinghua.lumaqq.qq.beans.QQUser;
import edu.tsinghua.lumaqq.qq.beans.ReceiveIMHeader;
import edu.tsinghua.lumaqq.qq.beans.SMS;
import edu.tsinghua.lumaqq.qq.beans.TempSessionIM;
import edu.tsinghua.lumaqq.qq.beans.UserPropertyChange;
import edu.tsinghua.lumaqq.qq.packets.BasicInPacket;
import edu.tsinghua.lumaqq.qq.packets.PacketParseException;
import edu.tsinghua.lumaqq.qq.packets.out.ReceiveIMReplyPacket;
/**
* <pre>
* 别人发来的消息包,如果是普通消息,格式为
* 1. 头部
* 2. 发送者QQ号,4字节
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,是好友发的,还是陌生人发的,还是系统消息等等, 2字节
* 8. 发送者QQ版本,2字节
* 9. 发送者的QQ号,4字节
* 10. 接受者的QQ号,4字节
* 11. md5处理的发送方的uid和session key,16字节
* 12. 普通消息类型,比如是文本消息还是其他什么消息,2字节
* 13. 会话ID,2字节,如果是一个操作需要发送多个包才能完成,则这个id必须一致
* 14. 发送时间,4字节
* 15. 发送者头像,2字节
* 16. 是否有字体属性,4字节,有一般是0x00000001
* 17. 消息的分片数,1字节
* 18. 分片序号,1字节,从0开始
* 19. 消息id,2字节,同一条消息的不同分片id相同
* 20. 消息类型,这里的类型表示是正常回复还是自动回复之类的信息, 1字节
* 21. 消息正文,长度 = 剩余字节数 - 包尾字体属性长度
* 22. 字体属性,和SendIMPacket中的相同
* 23. 尾部
*
* 如果是临时会话消息
* 2. 发送者QQ号,4字节
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,2字节,这里是0x001F
* 8. 发送者QQ号,4字节
* 9. 未知的4字节
* 10. 发送者昵称长度,1字节
* 11. 发送者昵称
* 12. Site名称长度,1字节
* 13. Site名称
* 14. 未知的1字节
* 15. 发送时间,4字节
* 16. 后面实际内容的长度,2字节
* Note: 包的结尾有4个未知字节,所以16部分的长度是实际内容长度,不包括那4个字节
* 17. 消息内容,长度 = 16部分 - 字体属性长度
* 18. 字体属性,参见SendIMPacket
* 19. 未知的4字节
* 20. 尾部
*
* 如果是群通知
* 1. 头部
* 2. 群的内部ID,4字节
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,是好友发的,还是陌生人发的,还是系统消息等等, 2字节,0x002C
* 8. 群的外部ID,4字节
* 9. 群类型,1字节
* 10. 操作码,1字节
* 11. 接受者QQ号,4字节
* 12. 接受者当前角色,1字节
* 13. 尾部
*
* 如果是个性签名改变通知
* 1. 头部
* 2. 发送者QQ号,在这里是10000,4字节
* 3. 接受者QQ号,4字节
* 4. 服务器端包序号,4字节
* 5. 发送者IP,4字节
* 6. 发送者端口,2字节
* 7. 消息类型,2字节,在这里是0x0041
* 8. 个性签名改变的QQ号,4字节
* 9. 个性签名改变的时间,4字节
* 10. 新个性签名的字节长度,1字节
* 11. 新个性签名
* 12. 尾部
*
* 如果是系统消息:
* 1-7. 与普通消息相同,只不过7是0x0030,表示是系统消息
* 8. 系统消息类型,1字节
* 9. 系统消息长度,1字节
* 10. 系统消息
*
* 如果是来自绑定手机的手机短消息,格式为:
* 1. 头部
* 2. 发送者QQ号,4字节
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,是好友发的,还是陌生人发的,还是系统消息等等, 2字节,0x000B
* 8. 未知1字节,0x0
* 9. 发送者QQ号,4字节
* 10. 发送者头像,2字节
* 11. 发送者名称,最多13字节,不足后面补0
* 12. 未知的1字节,0x4D
* 13. 消息内容,160字节,如果不足,填0
* 注:在接收长消息时,13部分前两个字节为固定内容,似乎是用来标识同一消息的分片,但是
* QQ本身却没有处理这个字段,而是把这个字段也显示出来了,似乎是个bug
* 14. 尾部
*
* 如果是来自移动QQ用户的手机短消息,格式为:
* 1. 头部
* 2. 发送者QQ号,4字节
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,是好友发的,还是陌生人发的,还是系统消息等等, 2字节,0x0013
* 8. 未知1字节
* 9. 发送者QQ号,4字节
* 10. 发送者头像,2字节
* 11. 发送者名称,最多13字节,不足后面补0
* 12. 未知的1字节,0x4D
* 13. 短信发送时间,4字节
* 14. 未知1字节,0x03
* 15. 短信内容,160字节,不足填0
* 注:在接收长消息时,15部分前两个字节为固定内容,似乎是用来标识同一消息的分片,但是
* QQ本身却没有处理这个字段,而是把这个字段也显示出来了,似乎是个bug
* 16. 尾部
*
* 如果是来自移动QQ用户的手机短消息(使用的是手机号码),格式为:
* 1. 头部
* 2. 发送者QQ号,4字节
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,是好友发的,还是陌生人发的,还是系统消息等等, 2字节,0x0014
* 8. 未知1字节
* 9. 移动QQ的手机号,18字节,不足补0
* 10. 未知的2字节
* 11. 短信发送时间,4字节
* 12. 未知1字节,0x03
* 13. 短信内容,160字节,不足填0
* 注:在接收长消息时,13部分前两个字节为固定内容,似乎是用来标识同一消息的分片,但是
* QQ本身却没有处理这个字段,而是把这个字段也显示出来了,似乎是个bug
* 14. 尾部
*
* 如果是来自普通手机的消息
* 1. 头部
* 2. 发送者QQ号,4字节,在这里是10000
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,是好友发的,还是陌生人发的,还是系统消息等等, 2字节,0x000C
* 8. 未知1字节,0x0
* 9. 发送者手机号码,20字节,不足填0
* 10. 短信内容,160字节,不足填0
* 注:在接收长消息时,15部分前两个字节为固定内容,似乎是用来标识同一消息的分片,但是
* QQ本身却没有处理这个字段,而是把这个字段也显示出来了,似乎是个bug
* 11. 尾部
*
* 如果是请求传送文件,格式为
* 1 - 18. 与普通消息格式相同,差别只有12部分,为0x0035,表示是请求传送文件
* 19. 未用部分,全0,15字节
* 20. 传输类型,1字节
* 21. 连接方式,1字节
* 22. 请求者外部ip,4字节
* 23. 请求者QQ端口,2字节,如果连接方式为0x3,则这个部分没有
* 24. 第一个监听端口,2字节
* 25. 请求者真实ip,4字节
* 26. 第二个监听端口,2字节
* 27. 空格符0x20
* 28. 分隔符0x1F
* 29. 文件名,不定长,不包含路径名
* 30. 分隔符0x1F
* 31. 文件字节数的字符串形式加“ 字节”
* 32. 尾部
*
* 如果是接受传送文件的请求,格式为
* 1 - 18. 与普通消息格式相同,差别只有12部分,对于UDP请求,为0x0037
* 19 - 26 和0x0035时相同
* 27. 尾部
*
* 如果是通知文件传送的端口,格式为
* 1 - 18. 与普通消息格式相同,差别只有12部分,为0x003B,表示是通知文件传送端口
* 19 - 26 和0x0035时相同
* 27. 尾部
*
* 未知类型的群消息
* 1-7. 与普通消息相同,只不过发送者QQ号相当于是群的内部ID,7部分为0x0020
* 8. 群外部ID,4字节
* 9. 群类型,1字节
* 10. 发送者QQ号,4字节
* 11. 未知的两字节,全0
* 12. 消息序号,2字节
* 13. 消息发送时间,4字节
* 14. 未知的4字节
* 15. 后面的数据的长度,2字节
* 16. 以0结尾的消息内容
* 17. 字体属性,和SendIMPacket中的相同
* 18. 尾部
*
* 如果是讨论组或者多人对话消息,讨论组和多人对话属于临时群,格式为
* 1 - 7 部分与普通消息相同,只不过7部分是0x002A
* 8. 父群内部ID,4字节
* 9. 群类型,1字节
* 10. 讨论组内部ID,4字节
* 11. 发送者QQ号,4字节
* 12. 未知的两字节,全0
* 13. 消息序号,2字节
* 14. 消息发送时间,4字节
* 15. Version ID, 4字节,所谓version id,是我这里根据这个字段的意思乱编的。
* 其作用主要是标识群信息的版本,比如一开始,群内有两个人,这个时候版本是0,然后我删
* 除一个人,那么群的版本就要加1,于是就变成了1。然后我又加了2个人,于是版本再加2变成3,
* 假如我再删一个人又加一个人,那么版本就要加2变成5了。为什么要加2呢,因为我做了两次操作,
* 一次删,一次加,所以版本号加了2,你可能奇怪:为什么我不一次搞定,然后版本加1啊?因为
* QQ的协议就是这样,它的协议不能同时做删除和添加成员的操作,晕吧?所以我要做两次操作,
* 也就是要发两个包才能又加人又删人,所以版本号加了2。客户端对每一个群都要保存这么一个version ID,
* 一旦收到的消息大于我本地的version id,于是就发个包过去请求得到目前的成员列表。所以你
* 会看到,QQ在发临时群消息的时候,成员如果变化了,会即时的反映出来,就是如此实现的了。
* 这个id不光是在修改成员的时候才变,修改基本信息也照样变,用来表示这个群的信息修改过了。
* 16. 后面内容的长度
* 17. Content Type, 2字节,0x0001表示纯文件,0x0002表示有自定义表情
* 18. 消息的分片数,1字节
* 19. 分片序号,1字节,从0开始
* 20. 消息id,2字节,同一条消息的不同分片id相同
* 21. 4字节,未知
* 22. 消息正文,长度 = 剩余字节数 - 包尾字体属性长度
* 23. 字体属性,和SendIMPacket中的相同
* 24. 尾部
*
* 如果是示范群或会员创建的群发来的消息,这些群都是固定群,格式为
* 1 - 7 部分与普通消息相同,只不过7部分是0x002B
* 8. 群外部ID,4字节
* 9. 群类型,1字节
* 10. 发送者QQ号,4字节
* 11. 未知的两字节
* 12. 消息序号,2字节
* 13. 消息发送时间,4字节
* 14. Version ID, 4字节
* 15. 后面内容的长度
* 16. Content Type, 2字节,0x0001表示纯文件,0x0002表示有自定义表情
* 17. 消息的分片数,1字节
* 18. 分片序号,1字节,从0开始
* 19. 消息id,2字节,同一条消息的不同分片id相同
* 20. 4字节,未知
* 21. 消息正文,长度 = 剩余字节数 - 包尾字体属性长度
* 22. 字体属性,和SendIMPacket中的相同
* 23. 尾部
*
* 如果是0x0041,可能是向服务器报告最后连接情况的包,格式为
* 1 - 27. 和0x003B时相同,差别只有命令不同,为0x0041
*
* 如果是取消传送文件请求,格式为:
* 1 - 18. 与普通消息格式相同,差别只有12部分,为0x0049,表示是取消传送文件
* 19. 未用部分,全0,15字节
* 20. 固定字节0x65
* 21. 尾部
*
* 如果是QQ直播消息:
* 1. 头部
* 2. 发送者QQ号,4字节,一般是10000
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,2字节,0x0018
* 8. 直播消息类型,2字节
* 9. 后面的内容长度,2字节,exclusive
* 如果8部分是0x0100,表示普通直播消息
* 10. 直播标题长度,1字节
* 11. 标题
* 12. 直播描述长度,1字节
* 13. 直播描述
* 14. 直播URL长度,1字节
* 15. 直播URL
* 16. 如果10-15部分加起来的长度没有超过9部分的值,则后面都填0
* 17. 尾部
* 如果8部分是0x0400,表示网络硬盘通知
* 10. 标题
* 11. 分隔符,1字节,0x02
* 12. 描述
* 13. 分隔符,1字节,0x02
* 14. 好友的QQ号的字符串形式
* 15. 尾部
*
* 如果是会员登录提示
* 1. 头部
* 2. 发送者QQ号,4字节,一般是10000
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,2字节,0x0012
* 8. 未知1字节
* 9. 尾部
*
* 如果是自定义头像变化通知
* 1. 头部
* 2. 发送者QQ号,4字节,一般是10000
* 3. 接收者QQ号,4字节
* 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
* 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
* 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
* 7. 消息类型,2字节,0x0049
* 8. 头像变化的好友个数,1字节
* 9. 好友QQ号,4字节
* 10. 自定义头像最后改变的时间戳,4字节,为从1970-1-1到现在的秒数
* 11. 自定义头像的MD5值,16字节
* 12. 如果有更多好友,重复10-11部分
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -