XMPP 服务器 Openfire 的 Emoji 支持问题(进行部分修改)
当前最新版3.9.3已经可以支持Emoji
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在为领航信息开发 eMessage 支持的时候,我们曾使用著名的开源 XMPP 服务器软件 Openfire。但在使用中遇到了几个问题,并通过修改源代码将这些问题解决掉了。接下来的几篇文章,我会介绍一下这些问题并讲述是如何解决掉的。
先介绍一下背景。XMPP 是一个开放的即时通讯协议,非常不错,有很多开源软件实现了 XMPP 协议,Openfire 算是实现得比较全的,而且安装配置比较容易。其他比较流行的开源 XMPP 服务器还有 Tigase 和 ejabberd。我们现在已经切换到了 ejabberd 上,毕竟 WhatsApp 最初也使用的是 ejabberd 嘛,呵呵。读者如果有兴趣,我也可以跟大家讲讲这几个 XMPP 服务器的区别和潜在问题。
问题描述
解决方案
- /**
- * Makes sure that each individual character is a valid XML character.
- *
- * Note that when MXParser is being modified to handle multibyte chars correctly, this method needs to change (as
- * then, there are more codepoints to check).
- */
- @Override
- protected char more() throws IOException, XmlPullParserException {
- final char codePoint = super.more(); // note - this does NOT return a codepoint now, but simply a (single byte) char
- er!
- if ((codePoint == 0x0) || // 0x0 is not allowed, but flash clients insist on sending this as the very first
- racter of a stream. We should stop allowing this codepoint after the first byte has been parsed.
- (codePoint == 0x9) ||
- (codePoint == 0xA) ||
- (codePoint == 0xD) ||
- ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
- ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))) {
- return codePoint;
- throw new XmlPullParserException("Illegal XML character: " + Integer.parseInt(codePoint+"", 16));
- }
先看看这个函数的作用。如注释所言,这个函数用来判定特定字符是否是一个合法的 XML 字符。XML 一般要求按照 UTF8 编码的方式存储和传输字符,在程序处理时,会直接转换成 UNICODE 的 UCS 形式,这样便于程序做处理。
- The following byte sequences are used to represent a character. The sequence to be used depends on the UCS code
- number of the character:
- 0x00000000 - 0x0000007F:
- 0xxxxxxx
- 0x00000080 - 0x000007FF:
- 110xxxxx 10xxxxxx
- 0x00000800 - 0x0000FFFF:
- 1110xxxx 10xxxxxx 10xxxxxx
- 0x00010000 - 0x001FFFFF:
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- 0x00200000 - 0x03FFFFFF:
- 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- 0x04000000 - 0x7FFFFFFF:
- 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
从上面的对应关系就可以知悉,UNICODE 可能的码位(code point 或者 code number)最大可以到 0x7FFFFFFF!而 Openfire 判断是否为合法 XML 字符的范围只到 0xFFFD!显然,不能显示正确处理 Emoji 字符的原因是 Openfire 将用来表示 Emoji 字符的那些码位范围给当成非法 XML 字符了。一种比较简单粗暴的修改办法是:
- /**
- * Makes sure that each individual character is a valid XML character.
- *
- * Note that when MXParser is being modified to handle multibyte chars correctly, this method needs to change (as
- * then, there are more codepoints to check).
- */
- @Override
- protected char more() throws IOException, XmlPullParserException {
- final char codePoint = super.more(); // note - this does NOT return a codepoint now, but simply a (single byte) char
- r!
- if ((codePoint == 0x0) || // 0x0 is not allowed, but flash clients insist on sending this as the very first
- acter of a stream. We should stop allowing this codepoint after the first byte has been parsed.
- (codePoint == 0x9) ||
- (codePoint == 0xA) ||
- (codePoint == 0xD) ||
- ((codePoint >= 0x20) && (codePoint <= 0xFFFD)) ||
- ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))) {
- return codePoint;
- }
- throw new XmlPullParserException("Illegal XML character: " + Integer.parseInt(codePoint+"", 16));
- }
但马上有高手指出,这个方法太粗暴了,可能带来一些安全隐患(参见:http://community.igniterealtime.org/thread/48846),所以更加正确的方法是:
- @Override
- protected char more() throws IOException, XmlPullParserException {
- final char codePoint = super.more(); // note - this does NOT return a codepoint now, but simply a (double byte) character!
- boolean validCodepoint = false;
- boolean isLowSurrogate = Character.isLowSurrogate(codePoint);
- if ((codePoint == 0x0)|| // 0x0 is not allowed, but flash clients insist on sending this as the very first character of a stream. We should stop allowing this codepoint after the first byte has been parsed.
- (codePoint == 0x9) ||
- (codePoint == 0xA) ||
- (codePoint == 0xD)||
- ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
- ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))) {
- validCodepoint = true;
- }
- else if (highSurrogateSeen) {
- if (isLowSurrogate) {
- validCodepoint = true;
- } else {
- throw new XmlPullParserException(
- "High surrogate followed by non low surrogate '0x"
- + String.format("%x", (int) codePoint) + "'");
- }
- }
- else if (isLowSurrogate) {
- throw new XmlPullParserException("Low surrogate '0x "+ String.format("%x", (int) codePoint)+ " without preceeding high surrogate");
- }
- else if (Character.isHighSurrogate(codePoint)) {
- highSurrogateSeen = true;// Return here so that highSurrogateSeen is not reset
- return codePoint;
- }
- // Always reset high surrogate seen
- highSurrogateSeen = false;
- if (validCodepoint)
- return codePoint;
- throw new XmlPullParserException("Illegal XML character '0x"+ String.format("%x", (int) codePoint) + "'");
- }
在 MySQL 中存储 Emoji 字符
后记
XMPP 服务器 Openfire 的 Emoji 支持问题(进行部分修改)的更多相关文章
- Strophe.js连接XMPP服务器Openfire、Tigase实现Web私聊、群聊(MUC)
XMPP(Extensible Messaging and Presence Protocol)是一种网络即时通讯协议,它基于XML,具有很强的扩展性,被广泛使用在即时通讯软件.网络游戏聊天.Web聊 ...
- xmpp和OpenFire示例,即时聊天室,支持离线消息
让我说说为什么写这个博客,这是因为我在上周末的研究XMPP和OpenFire,从互联网上下载Demo,但跑不起来.它花了很长的时间.它被改造.抬高.篇博文也是希望后边学习XMPP和OpenFire的同 ...
- 技术笔记:XMPP之openfire+spark+smack
在即时通信这个领域目前只找到一个XMPP协议,在其协议基础上还是有许多成熟的产品,而且是开源的.所以还是想在这个领域多多了解一下. XMPP协议:具体的概念我就不写了,毕竟这东西网上到处是.简单的说就 ...
- IOS Socket 05-XMPP开始&安装服务器openfire&安装配置客户端
1. 即时通讯技术简介(IM) 即时通讯技术(IM-Instant Messageing)支持用户在线实时交谈.如果要发送一条信息,用户需要打开一个小窗口,以便让用户及其朋友在其中输入信息并让交谈双方 ...
- 常用的XMPP服务器
1. Openfire (Wildfire) 3.x 底层通讯采用的mina框架,minak框架其实性能一般,netty早已经超越它,虽然最初都是Doug Lea写的.3.4版本之后支持集群,单台服务 ...
- XMPP 和 OpenFire
XMPP XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测.是一种数据传输协议. XMPP的前身是Jabber,一个开源形式组织产生的网络 ...
- 基于MAC10.12+MYSQL5.7.17搭建XMPP服务器【黑苹果系统】
在以前的公司中了解到XMPP可以搭建即时通讯APP.出于好奇自己在空余时间也学了一下搭建XMPP服务器,其中遇到了许多问题,经过坎坷的路程终于搭建成功[这些坎坷的经历主要是由于自己的无知造成的] 下面 ...
- 在MAC中安装XMPP服务器
一.安装MySQL 1.下载安装包
- Android基于XMPP Smack openfire 开发的聊天室
Android基于XMPP Smack openfire 开发的聊天室(一)[会议服务.聊天室列表.加入] http://blog.csdn.net/lnb333666/article/details ...
随机推荐
- [LeetCode]题解(python):073-Set Matrix Zeroes
题目来源: https://leetcode.com/problems/set-matrix-zeroes/ 题意分析: 输入一个m×n矩阵,如果出现有0,那么将对应的行和列都变成0. 题目思路: 简 ...
- CentOS5.4下安装codeblocks 12.11
centos6.3下安装codeblock简单多了,这些开源的软件也都在不断进步.原来装过codeblocks10.05,忘了,这次安装又花了我半天时间,最后总算搞定. 先是安装了wxGTK-2.8. ...
- IOS UIActionSheet的使用方法
在IOS的用户接口向导中,苹果提供了另外一种显示警告框的手法,叫做UIActionSheet.它和UIAlertView比起来不会显得过于急切和紧张.而是很温和地在继续流程之前给用户提供了诸多选择. ...
- MacOS下使用VMware5 破解 安装win7 ISO 激活
VMware5 下载 破解 以及win7 ISO版本的安装 激活VMware5 下载与破解参考方法http://www.macx.cn/thread-2060440-1-1.htmlVMware5 是 ...
- 没有开发者账号,如何解锁wp8设备
原文 http://www.cnblogs.com/vsdot/p/3263454.html 问题的引入 好了,问题的由来是这样的,我想把我开发的wp8手机部署到手机上,可是竟然出现了下面的问题: [ ...
- File类常用方法
File类是IO中常用的类 先介绍几个常用的方法: public boolean canRead(),public boolean canWrite() 测试当前文件是否可读可写,若是则返回true ...
- leetcode第一刷_Construct Binary Tree from Inorder and Postorder Traversal
这道题是为数不多的感觉在读本科的时候见过的问题. 人工构造的过程是如何呢.兴许遍历最后一个节点一定是整棵树的根节点.从中序遍历中查找到这个元素,就能够把树分为两颗子树,这个元素左側的递归构造左子树,右 ...
- C#中文件管理的运用(Twelfth Day)
又到了总结的时间了,今天在云和学院学习了文件管理的一些运用及复习昨天学的里氏转换.今天我就总结下昨天遗留下的问题以及今天所学的知识. 昨天遗留的问题 里氏转换(父类转子类) 例:在这里定义父类Peop ...
- BZOJ 1677: [Usaco2005 Jan]Sumsets 求和( dp )
完全背包.. --------------------------------------------------------------------------------------- #incl ...
- hadoop序列化机制与java序列化机制对比
1.采用的方法: java序列化机制采用的ObjectOutputStream 对象上调用writeObject() 方法: Hadoop 序列化机制调用对象的write() 方法,带一个DataOu ...