Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f9824b283 | ||
|
|
c7e20632a8 | ||
|
|
81a2ab8239 | ||
|
|
f62e3f3cef | ||
|
|
5ce8165514 | ||
|
|
106756ded4 | ||
|
|
3f93be0ece | ||
|
|
a53f3eb587 | ||
|
|
d43afb282e | ||
|
|
2f195440cf | ||
|
|
d241acf980 | ||
|
|
debd4f1aea | ||
|
|
5a2ded4b6e | ||
|
|
ca1bdc9ee5 | ||
|
|
b187151683 | ||
|
|
c3859a9fae | ||
|
|
c233304a8f | ||
|
|
affc527326 | ||
|
|
07fb4ce511 | ||
|
|
ce9f2cbafe | ||
|
|
2ed81ea629 | ||
|
|
2454067fc3 | ||
|
|
21a0de04ca | ||
|
|
f8153d68e8 | ||
|
|
a909e79c50 | ||
|
|
c58790ebce |
@@ -90,4 +90,3 @@
|
||||
## License
|
||||
|
||||
1. Under the Creative Commons Attribution-NoDerivs 3.0 Unported license. See the [LICENSE](https://github.com/wildfirechat/server/blob/wildfirechat/LICENSE) file for details.
|
||||
2. Under the Anti 996 License. See the [Anti 996 LICENSE](https://github.com/wildfirechat/server/blob/wildfirechat/LICENSE_996) file for details.
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
</join>
|
||||
</network>
|
||||
|
||||
<properties>
|
||||
<property name="hazelcast.logging.type">slf4j</property>
|
||||
</properties>
|
||||
|
||||
<!-- map eviction -->
|
||||
<!-- http://docs.hazelcast.org/docs/latest-development/manual/html/Distributed_Data_Structures/Map/Map_Eviction.html -->
|
||||
<map name="messages_map">
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
#*********************************************************************
|
||||
#服务器的接入IP。给客户端提供是${server.ip}和${http_port}。
|
||||
#客户端会从${http_port}端口获取到长链接端口。这个地址一定要改成客户端可以访问到的IP地址
|
||||
# (如果您部署云服务器上或者具有独立公网出口的服务器上,请改为对应的公网IP;如果您部署在在内网环境下,在内网使用,这个地方改成内网地址)
|
||||
#(如果您部署云服务器上或者具有独立公网出口的服务器上,请改为对应的公网IP;如果您部署在在内网环境下,在内网使用,这个地方改成内网地址)
|
||||
server.ip 192.168.3.124
|
||||
|
||||
##原生客户端长链接端口
|
||||
port 1883
|
||||
|
||||
##客户端短链接端口
|
||||
##客户端短链接端口,客户端固定使用80端口,不要修改此端口。
|
||||
http_port 80
|
||||
|
||||
#管理端口
|
||||
@@ -32,15 +32,15 @@ host 0.0.0.0
|
||||
##本地绑定端口
|
||||
local_port 80
|
||||
|
||||
##是否使用内置DB。0使用mysql;1使用h2db;2使用mysql+mongodb。社区版只支持0和1,专业版支持2
|
||||
##是否使用内置DB。0使用mysql;1使用h2db。
|
||||
embed.db 1
|
||||
|
||||
## 是否自动清理历史消息记录,当为true时,IM服务会定时扫描t_messages_x和t_user_messages_x表,清除掉3年前的历史消息。
|
||||
## 如果不开启自动清理,需要手动清理t_messages_x和t_user_messages_x表内的历史数据,保存数据不能超过3年。
|
||||
## 使用mongodb时次开关无效(因为mongodb设置了消息过期时间,会自动清理)。
|
||||
## 使用mongodb时次开关无效(因为mongodb设置了消息过期时间,会自动清理,仅专业版支持mongodb)。
|
||||
db.auto_clean_history_messages true
|
||||
|
||||
##服务器管理接口密钥
|
||||
##服务器管理接口密钥。如果修改此端口需要同步修改使用管理API的地方,比如应用服务。
|
||||
http.admin.secret_key 123456
|
||||
|
||||
##服务器API接口参数是否检查时间。当设置为false时,所有的请求会检查时间的有效性;当设置为true时,可以在http.admin.secret_key保持不变的情况下,使用固定的服务API签名
|
||||
@@ -74,14 +74,21 @@ message.blacklist.strategy 0
|
||||
##是否禁止服务器端消息搜索,该功能暂未实现。目前的影响是如果打开,则存储消息时不单独保存_searchable_content字段
|
||||
message.disable_remote_search false
|
||||
|
||||
##允许客户端撤回用户自己发送的消息时限,单位是秒。群组管理员或群主撤回群成员消息、server api不受此限制,可以撤回任意时限内的消息
|
||||
##是否数据库中加密消息内容。注意这个开关在服务运行起来后不能改变了,避免读取数据库时无法恢复历史消息。
|
||||
##如果要避免数据库中明文存储消息内容,请打开这个开关和message.disable_remote_search开关
|
||||
message.encrypt_message_content false
|
||||
|
||||
##允许客户端撤回用户自己发送的消息时限,单位是秒。
|
||||
##Server API不受此限制,可以撤回任意时限内的消息
|
||||
##群组管理员或群主撤回群成员消息不受此限制,可以撤回任意时限内的消息。但管理员或群主撤回自己的消息还是需要准守此时间限制。
|
||||
##修改此参数需要同步修改客户端UI,客户端UI代码有判断撤回是否显示的逻辑。
|
||||
message.recall_time_limit 120
|
||||
|
||||
##禁止群主/群管理员撤回群内消息,关闭时群主/群管理员可以撤回任意时间段内群内成员的消息,群主/群管理员撤回自己发送的消息还是会受到message.recall_time_limit时间限制。
|
||||
##禁止群主/群管理员撤回群内消息,关闭时群主/群管理员可以撤回任意时间段内群内成员的消息。
|
||||
message.disable_group_manager_recall false
|
||||
|
||||
## 禁止客户端发送消息类型,消息类型以逗号分开,下面3个值为示例,可以删除。
|
||||
## 一般用于重要类型消息,比如交易、充值等,禁止客户端发送,仅限于服务器发送,提高安全性。
|
||||
##禁止客户端发送消息类型,消息类型以逗号分开,下面3个值为示例,可以删除。
|
||||
##一般用于重要类型消息,比如交易、充值等,禁止客户端发送,仅限于服务器发送,提高安全性。
|
||||
#message.forbidden_client_send_types 3999,4000,4001
|
||||
|
||||
## 当禁止发送消息时,允许例外发送的消息。这种一般用户需要被动回应的消息。比如群中被禁言的用户,他无法主动发起音视频,但可以接听未被禁言用户的音视频
|
||||
@@ -114,7 +121,7 @@ friend.request_expiration_duration 604800000
|
||||
##添加好友后会发送欢迎消息,比如"你们已经是好友了,可以聊天了"。这个开关控制是用旧的方式还是用新的方式。
|
||||
##旧的方式会发送notification消息(contenttype是80),内容会根据好友接收者的语言选择对应的语言,如果双方语言不一致会导致另外一方看到的不是本地语言。
|
||||
##新的方式会发送添加好友消息(contenttype是93)和欢迎消息(contenttype是92),需要客户端实现这两种消息。
|
||||
##如果客户端已经实现这两种消息可以打开这个开关
|
||||
##如果客户端已经实现这两种消息可以打开这个开关(客户端2020.6.30添加了此消息类型)。
|
||||
friend.new_welcome_message true
|
||||
|
||||
## 聊天室观众空闲退出时间,单位为毫秒,默认为15分钟,0为永远不退出
|
||||
@@ -151,7 +158,7 @@ hazelcast.configuration config/hazelcast.xml
|
||||
# Media server configuration
|
||||
#*********************************************************************
|
||||
##是否使用七牛云存储。1使用七牛;0使用内存文件服务器。默认的七牛账户信息不可用,请在七牛官网申请账户并配置
|
||||
## https://docs.wildfirechat.cn/server/media_server.html
|
||||
##配置使用七牛请参考:https://docs.wildfirechat.cn/server/media_server.html
|
||||
media.server.use_qiniu 0
|
||||
|
||||
##媒体类型分类
|
||||
@@ -196,11 +203,17 @@ local.media.storage.root media
|
||||
# 尽管内置文件存储可以使用,但是我们还是建议使用专业级别的对象存储服务,社区版可以使用七牛,专业版另外支持阿里云和也是私有对象存储。详情请参考 https://docs.wildfirechat.cn/server/oss.html
|
||||
# local.media.storage.remote_server_url https://example.com/media
|
||||
|
||||
# 是否支持多端登陆,为true时支持任意平台任意多个客户端同时登录;为false时每个平台只支持一个端登录,但不同平台可以同时登录。
|
||||
# 是否支持任意多端登陆,为true时支持任意平台任意多个客户端同时登录;为false时每个平台只支持一个端登录,但不同平台可以同时登录。
|
||||
# Android/iOS为移动平台,windows/mac/linux为pc平台,web为web平台,小程序为小程序平台。
|
||||
# 建议使用false
|
||||
server.multi_endpoint false
|
||||
|
||||
# 多平台连接状态通知(仅当multi_endpoint为false时有效),true时移动端可以收到pc或web端登录的通知。
|
||||
server.multi_platform_notification false
|
||||
server.multi_platform_notification true
|
||||
|
||||
# 当pc或者web在线时,手机是否默认静音,默认为true。注意仅影响默认值,用户可以手动切换是否静音。
|
||||
# 如果这里改为false,客户端那边需要同步修改pc在线默认通知状态,方法是client的setDefaultSilentWhenPcOnline函数。
|
||||
server.mobile_default_silent_when_pc_online true
|
||||
|
||||
#*********************************************************************
|
||||
# Push server configuration
|
||||
@@ -217,6 +230,8 @@ push.ios.server.address http://localhost:8085/ios/push
|
||||
#*********************************************************************
|
||||
# 监控配置
|
||||
#*********************************************************************
|
||||
##异常事件产生回调
|
||||
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||
#monitor.exception_event_address http://localhost:8888/im_exception_event/
|
||||
|
||||
#*********************************************************************
|
||||
@@ -276,7 +291,7 @@ sensitive.filter.type 0
|
||||
#sensitive.remote_server_url http://192.168.3.202:8888/message/censor
|
||||
## 需要进行敏感词审核的消息类型,当有多个时以英文逗号分割
|
||||
#sensitive.remote_sensitive_message_type 1,2,3
|
||||
## 如果远程审核服务返回403,返回给发送端发送失败还是成功?实际上这个值影响发送响应是否等待审核结果,当为false时,消息发送给审核服务就立即返回
|
||||
## 成功给客户端。当为true时,消息发送给审核服务,等待审核结果,如果结果为继续发送就返回给客户端为成功,结果为禁止发送就返回失败给客户端。
|
||||
## 如果远程审核服务返回403,返回给发送端发送失败还是成功?实际上这个值影响发送响应是否等待审核结果,当为false时,消息发送给审核服务就立即返回成功给客户端。
|
||||
## 当为true时,消息发送给审核服务,等待审核结果,如果结果为继续发送就返回给客户端为成功,结果为禁止发送就返回失败给客户端。
|
||||
## 客户端操作的超时一般为10s左右,且考虑到复杂的网络情况,建议处理时间不要超过3s。建议这个值为false。
|
||||
#sensitive.remote_fail_when_matched false
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.67</version>
|
||||
<version>0.70</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moquette-broker</artifactId>
|
||||
|
||||
@@ -46,21 +46,28 @@ public class FileAction extends Action {
|
||||
return true;
|
||||
}
|
||||
|
||||
final File file = getFileByPath(request.getPath());
|
||||
Logger.debug("Client [{}] get file [{}]", request.getIp(), file.getPath());
|
||||
File file = null;
|
||||
try {
|
||||
file = getFileByPath(request.getPath());
|
||||
} catch (Exception e) {
|
||||
response.sendError(HttpResponseStatus.NOT_FOUND, "404 File not found!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 隐藏文件,跳过
|
||||
if (file.isHidden() || !file.exists()) {
|
||||
// 隐藏文件或不存在,跳过
|
||||
if (file == null || file.isHidden() || !file.exists()) {
|
||||
response.sendError(HttpResponseStatus.NOT_FOUND, "404 File not found!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 非文件,跳过
|
||||
if (false == file.isFile()) {
|
||||
if (!file.isFile()) {
|
||||
response.sendError(HttpResponseStatus.FORBIDDEN, "403 Forbidden!");
|
||||
return true;
|
||||
}
|
||||
|
||||
Logger.debug("Client [{}] get file [{}]", request.getIp(), file.getPath());
|
||||
|
||||
// Cache Validation
|
||||
String ifModifiedSince = request.getHeader(HttpHeaderNames.IF_MODIFIED_SINCE.toString());
|
||||
if (StrUtil.isNotBlank(ifModifiedSince)) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.common.APIPath;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
@@ -46,7 +47,11 @@ abstract public class AdminAction extends Action {
|
||||
if (!mLimitCounter.isGranted("admin")) {
|
||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
}
|
||||
|
||||
|
||||
if(APIPath.Health.equals(request.getUri())) {
|
||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
String nonce = request.getHeader("nonce");
|
||||
if (StringUtil.isNullOrEmpty(nonce)) {
|
||||
nonce = request.getHeader("Nonce");
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.common.APIPath;
|
||||
import cn.wildfirechat.common.ErrorCode;
|
||||
import cn.wildfirechat.pojos.InputUpdateFriendExtra;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
|
||||
@Route(APIPath.Friend_Set_Extra)
|
||||
@HttpMethod("POST")
|
||||
public class FriendExtraPutAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputUpdateFriendExtra input = getRequestBody(request.getNettyRequest(), InputUpdateFriendExtra.class);
|
||||
ErrorCode errorCode = messagesStore.setFriendExtraRequest(input.getOperator(), input.getTargetId(), input.getExtra(), new long[1]);
|
||||
setResponseContent(RestResult.resultOf(errorCode), response);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
|
||||
import cn.wildfirechat.common.APIPath;
|
||||
import cn.wildfirechat.pojos.OutputUserBlockStatusList;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
|
||||
import static java.lang.management.ManagementFactory.getOperatingSystemMXBean;
|
||||
|
||||
@Route(APIPath.Health)
|
||||
public class HealthAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
|
||||
jsonObject.put("node", "1");
|
||||
|
||||
JSONObject cpuObject = new JSONObject();
|
||||
jsonObject.put("cpu", cpuObject);
|
||||
|
||||
JSONObject memoryObject = new JSONObject();
|
||||
jsonObject.put("memory", memoryObject);
|
||||
|
||||
JSONObject fileObject = new JSONObject();
|
||||
jsonObject.put("disk", fileObject);
|
||||
|
||||
if (getOperatingSystemMXBean() instanceof OperatingSystemMXBean) {
|
||||
double load = getOperatingSystemMXBean().getSystemLoadAverage();
|
||||
cpuObject.put("load", load);
|
||||
}
|
||||
cpuObject.put("cores", Runtime.getRuntime().availableProcessors());
|
||||
|
||||
memoryObject.put("free", Runtime.getRuntime().freeMemory());
|
||||
memoryObject.put("max", Runtime.getRuntime().maxMemory());
|
||||
memoryObject.put("avail", Runtime.getRuntime().totalMemory());
|
||||
|
||||
File root = new File("/");
|
||||
fileObject.put("space", root.getTotalSpace());
|
||||
fileObject.put("free", root.getFreeSpace());
|
||||
fileObject.put("usable", root.getUsableSpace());
|
||||
|
||||
JSONArray out = new JSONArray();
|
||||
out.add(jsonObject);
|
||||
RestResult result = RestResult.ok(out);
|
||||
setResponseContent(result, response);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,6 @@ public final class BrokerConstants {
|
||||
public static final String DISABLED_PORT_BIND = "disabled";
|
||||
public static final String HOST = "0.0.0.0";
|
||||
public static final String NEED_CLIENT_AUTH = "need_client_auth";
|
||||
public static final String HAZELCAST_CONFIGURATION = "hazelcast.configuration";
|
||||
public static final String HAZELCAST_CLIENT_IP = "hazelcast.client.ip";
|
||||
public static final String HAZELCAST_CLIENT_PORT = "hazelcast.client.port";
|
||||
public static final String NETTY_SO_BACKLOG_PROPERTY_NAME = "netty.so_backlog";
|
||||
@@ -132,6 +131,7 @@ public final class BrokerConstants {
|
||||
|
||||
public static final String SERVER_MULTI_ENDPOINT = "server.multi_endpoint";
|
||||
public static final String SERVER_MULTI_PLATFROM_NOTIFICATION = "server.multi_platform_notification";
|
||||
public static final String SERVER_MOBILE_DEFAULT_SILENT_WHEN_PC_ONLINE = "server.mobile_default_silent_when_pc_online";
|
||||
|
||||
public static final String MESSAGE_ROAMING = "message.roaming";
|
||||
public static final String MESSAGE_Remote_History_Message = "message.remote_history_message";
|
||||
@@ -167,6 +167,8 @@ public final class BrokerConstants {
|
||||
public static final String MESSAGES_GLOBAL_MUTE_EXCEPTION_TYPES = "message.global_mute_exception_types";
|
||||
|
||||
public static final String MESSAGES_DISABLE_REMOTE_SEARCH = "message.disable_remote_search";
|
||||
public static final String MESSAGES_ENCRYPT_MESSAGE_CONTENT = "message.encrypt_message_content";
|
||||
|
||||
|
||||
public static final String MESSAGES_RECALL_TIME_LIMIT = "message.recall_time_limit";
|
||||
public static final String MESSAGES_DISABLE_GROUP_MANAGER_RECALL = "message.disable_group_manager_recall";
|
||||
|
||||
@@ -37,7 +37,9 @@ public class HandleFriendRequestHandler extends IMHandler<WFCMessage.HandleFrien
|
||||
long timestamp = System.currentTimeMillis();
|
||||
builder.setMessageId(messageId);
|
||||
builder.setServerTimestamp(timestamp);
|
||||
saveAndPublish(request.getTargetUid(), null, builder.build());
|
||||
if(!StringUtil.isNullOrEmpty(builder.getContent().getSearchableContent())) {
|
||||
saveAndPublish(request.getTargetUid(), null, builder.build());
|
||||
}
|
||||
|
||||
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
|
||||
String language = "zh_CN";
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
package io.moquette.persistence;
|
||||
|
||||
import cn.wildfirechat.common.IMExceptionEvent;
|
||||
import cn.wildfirechat.pojos.SystemSettingPojo;
|
||||
import cn.wildfirechat.proto.ProtoConstants;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import cn.wildfirechat.server.ThreadPoolExecutorWrapper;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.core.MultiMap;
|
||||
import com.hazelcast.util.StringUtil;
|
||||
@@ -26,7 +26,9 @@ import win.liyufan.im.MessageBundle;
|
||||
import win.liyufan.im.MessageShardingUtil;
|
||||
import win.liyufan.im.Utility;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
@@ -42,10 +44,15 @@ public class DatabaseStore {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DatabaseStore.class);
|
||||
private final ThreadPoolExecutorWrapper mScheduler;
|
||||
private boolean disableRemoteMessageSearch = false;
|
||||
private boolean encryptMessage = false;
|
||||
public void setDisableRemoteMessageSearch(boolean disableRemoteMessageSearch) {
|
||||
this.disableRemoteMessageSearch = disableRemoteMessageSearch;
|
||||
}
|
||||
|
||||
public void setEncryptMessage(boolean encryptMessage) {
|
||||
this.encryptMessage = encryptMessage;
|
||||
}
|
||||
|
||||
public DatabaseStore(ThreadPoolExecutorWrapper scheduler) {
|
||||
this.mScheduler = scheduler;
|
||||
}
|
||||
@@ -537,7 +544,7 @@ public class DatabaseStore {
|
||||
statement.setString(index++, message.getConversation().getTarget());
|
||||
statement.setInt(index++, message.getConversation().getLine());
|
||||
Blob blob = connection.createBlob();
|
||||
blob.setBytes(1, message.getContent().toByteArray());
|
||||
blob.setBytes(1, encryptMessageContent(message.getContent().toByteArray(), false));
|
||||
statement.setBlob(index++, blob);
|
||||
if (!disableRemoteMessageSearch) {
|
||||
statement.setString(index++, searchableContent);
|
||||
@@ -571,6 +578,15 @@ public class DatabaseStore {
|
||||
});
|
||||
}
|
||||
|
||||
byte[] encryptMessageContent(byte[] in, boolean force) {
|
||||
if(in != null && (encryptMessage || force)) {
|
||||
for (int i = 0; i < in.length; i++) {
|
||||
in[i] ^= 0xBD;
|
||||
}
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
void persistSensitiveMessage(final WFCMessage.Message message) {
|
||||
if(message.getContent().getPersistFlag() == Transparent) {
|
||||
return;
|
||||
@@ -599,7 +615,7 @@ public class DatabaseStore {
|
||||
statement.setString(index++, message.getConversation().getTarget());
|
||||
statement.setInt(index++, message.getConversation().getLine());
|
||||
Blob blob = connection.createBlob();
|
||||
blob.setBytes(1, message.getContent().toByteArray());
|
||||
blob.setBytes(1, encryptMessageContent(message.getContent().toByteArray(), false));
|
||||
statement.setBlob(index++, blob);
|
||||
statement.setString(index++, searchableContent);
|
||||
statement.setTimestamp(index++, new Timestamp(message.getServerTimestamp()));
|
||||
@@ -668,7 +684,7 @@ public class DatabaseStore {
|
||||
builder.setConversation(cb.build());
|
||||
Blob blob = resultSet.getBlob(index++);
|
||||
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.parseFrom(blob.getBinaryStream());
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.parseFrom(encryptMessageContent(toByteArray(blob.getBinaryStream()), false));
|
||||
builder.setContent(messageContent);
|
||||
builder.setServerTimestamp(resultSet.getTimestamp(index++).getTime());
|
||||
WFCMessage.Message message = builder.build();
|
||||
@@ -702,6 +718,16 @@ public class DatabaseStore {
|
||||
return out;
|
||||
}
|
||||
|
||||
public byte[] toByteArray(InputStream input) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[4096];
|
||||
int n = 0;
|
||||
while (-1 != (n = input.read(buffer))) {
|
||||
output.write(buffer, 0, n);
|
||||
}
|
||||
return output.toByteArray();
|
||||
}
|
||||
|
||||
MessageBundle getMessage(long messageId) {
|
||||
String sql = "select `_from`, `_type`, `_target`, `_line`, `_data`, `_dt` from " + MessageShardingUtil.getMessageTable(messageId) +" where _mid = ? order by `_dt` DESC limit 1";
|
||||
Connection connection = null;
|
||||
@@ -724,7 +750,7 @@ public class DatabaseStore {
|
||||
builder.setConversation(cb.build());
|
||||
Blob blob = resultSet.getBlob(index++);
|
||||
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.parseFrom(blob.getBinaryStream());
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.parseFrom(encryptMessageContent(toByteArray(blob.getBinaryStream()), false));
|
||||
builder.setContent(messageContent);
|
||||
builder.setServerTimestamp(resultSet.getTimestamp(index++).getTime());
|
||||
WFCMessage.Message message = builder.build();
|
||||
@@ -829,7 +855,8 @@ public class DatabaseStore {
|
||||
builder.setConversation(cb.build());
|
||||
Blob blob = resultSet.getBlob(index++);
|
||||
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.parseFrom(blob.getBinaryStream());
|
||||
WFCMessage.MessageContent messageContent = WFCMessage.MessageContent.parseFrom(encryptMessageContent(toByteArray(blob.getBinaryStream()), false));
|
||||
|
||||
builder.setContent(messageContent);
|
||||
|
||||
builder.setServerTimestamp(resultSet.getTimestamp(index++).getTime());
|
||||
|
||||
@@ -133,6 +133,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
private boolean mFriendNewWelcomeMessage = true;
|
||||
|
||||
private boolean mMultiPlatformNotification = false;
|
||||
private boolean mMobileDefaultSilentWhenPCOnline = true;
|
||||
private boolean mDisableStrangerChat = false;
|
||||
|
||||
private long mChatroomParticipantIdleTime = 900000;
|
||||
@@ -183,6 +184,12 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
printMissConfigLog(SERVER_MULTI_PLATFROM_NOTIFICATION, mMultiPlatformNotification + "");
|
||||
}
|
||||
|
||||
try {
|
||||
mMobileDefaultSilentWhenPCOnline = Boolean.parseBoolean(m_Server.getConfig().getProperty(SERVER_MOBILE_DEFAULT_SILENT_WHEN_PC_ONLINE, "true"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
mDisableSearch = Boolean.parseBoolean(m_Server.getConfig().getProperty(BrokerConstants.FRIEND_Disable_Search));
|
||||
} catch (Exception e) {
|
||||
@@ -268,6 +275,12 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
boolean encryptMessage = Boolean.parseBoolean(m_Server.getConfig().getProperty(BrokerConstants.MESSAGES_ENCRYPT_MESSAGE_CONTENT, "false"));
|
||||
databaseStore.setEncryptMessage(encryptMessage);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
mRecallTimeLimit = Long.parseLong(m_Server.getConfig().getProperty(BrokerConstants.MESSAGES_RECALL_TIME_LIMIT));
|
||||
} catch (Exception e) {
|
||||
@@ -3066,6 +3079,43 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorCode setFriendExtraRequest(String fromUser, String targetUserId, String extra, long[] heads){
|
||||
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
||||
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
|
||||
|
||||
FriendData friendData = null;
|
||||
Collection<FriendData> friends = friendsMap.get(fromUser);
|
||||
if (friends == null || friends.size() == 0) {
|
||||
friends = loadFriend(friendsMap, fromUser);
|
||||
}
|
||||
|
||||
for (FriendData fd:friends) {
|
||||
if (fd.getFriendUid().equals(targetUserId)) {
|
||||
friendData = fd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (friendData == null) {
|
||||
friendData = new FriendData();
|
||||
friendData.setUserId(fromUser);
|
||||
friendData.setFriendUid(targetUserId);
|
||||
}
|
||||
|
||||
friendData.setExtra(extra);
|
||||
friendData.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
databaseStore.persistOrUpdateFriendData(friendData);
|
||||
|
||||
heads[0] = friendData.getTimestamp();
|
||||
|
||||
friendsMap.remove(fromUser);
|
||||
|
||||
callbackRelationEvent(fromUser, targetUserId, 3, extra);
|
||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorCode handleJoinChatroom(String userId, String clientId, String chatroomId) {
|
||||
IMap<String, WFCMessage.ChatroomInfo> chatroomInfoMap = m_Server.getHazelcastInstance().getMap(CHATROOMS);
|
||||
@@ -3394,9 +3444,9 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
public boolean getSilentWhenPcOnline(String userId) {
|
||||
WFCMessage.UserSettingEntry entry = getUserSetting(userId, UserSettingScope.kUserSettingMuteWhenPCOnline, null);
|
||||
if (entry != null && "1".equals(entry.getValue())) {
|
||||
return true;
|
||||
return !mMobileDefaultSilentWhenPCOnline;
|
||||
} else {
|
||||
return false;
|
||||
return mMobileDefaultSilentWhenPCOnline;
|
||||
}
|
||||
}
|
||||
private Date getTodayDate() {
|
||||
|
||||
@@ -362,7 +362,7 @@ public class Server {
|
||||
LOG.info("Configuring embedded Hazelcast instance");
|
||||
String serverIp = getServerIp(config);
|
||||
|
||||
String hzConfigPath = config.getProperty(BrokerConstants.HAZELCAST_CONFIGURATION);
|
||||
String hzConfigPath = "config/hazelcast.xml";
|
||||
String hzClientIp = config.getProperty(BrokerConstants.HAZELCAST_CLIENT_IP, "localhost");
|
||||
String hzClientPort = config.getProperty(BrokerConstants.HAZELCAST_CLIENT_PORT, "5703");
|
||||
|
||||
|
||||
@@ -84,34 +84,37 @@ class ConfigurationParser {
|
||||
String line;
|
||||
try {
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
int commentMarker = line.indexOf('#');
|
||||
if (commentMarker != -1) {
|
||||
if (commentMarker == 0) {
|
||||
// skip its a comment
|
||||
continue;
|
||||
} else {
|
||||
// it's a malformed comment
|
||||
throw new ParseException(line, commentMarker);
|
||||
String[] ss = line.split(" ");
|
||||
if(ss.length != 2) {
|
||||
throw new ParseException(line, commentMarker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (line.isEmpty() || line.matches("^\\s*$")) {
|
||||
// skip it's a black line
|
||||
continue;
|
||||
}
|
||||
|
||||
// split till the first space
|
||||
int delimiterIdx = line.indexOf(' ');
|
||||
String key;
|
||||
String value = "";
|
||||
if (delimiterIdx > 0) {
|
||||
key = line.substring(0, delimiterIdx).trim();
|
||||
value = line.substring(delimiterIdx).trim();
|
||||
} else {
|
||||
key = line.trim();
|
||||
}
|
||||
|
||||
m_properties.put(key, value);
|
||||
}
|
||||
|
||||
// split till the first space
|
||||
int delimiterIdx = line.indexOf(' ');
|
||||
String key;
|
||||
String value = "";
|
||||
if (delimiterIdx > 0) {
|
||||
key = line.substring(0, delimiterIdx).trim();
|
||||
value = line.substring(delimiterIdx).trim();
|
||||
} else {
|
||||
key = line.trim();
|
||||
}
|
||||
|
||||
m_properties.put(key, value);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ParseException("Failed to read", 1);
|
||||
|
||||
@@ -182,6 +182,7 @@ public interface IMessagesStore {
|
||||
ErrorCode SyncFriendRequestUnread(String userId, long unreadDt, long[] head);
|
||||
ErrorCode isAllowUserMessage(String fromUser, String userId);
|
||||
ErrorCode setFriendAliasRequest(String fromUser, String targetUserId, String alias, long[] head);
|
||||
ErrorCode setFriendExtraRequest(String fromUser, String targetUserId, String extra, long[] head);
|
||||
|
||||
ErrorCode handleJoinChatroom(String userId, String clientId, String chatroomId);
|
||||
ErrorCode handleQuitChatroom(String userId, String clientId, String chatroomId);
|
||||
|
||||
@@ -108,7 +108,7 @@ public class DBUtil {
|
||||
|
||||
Flyway flyway = Flyway.configure().dataSource(comboPooledDataSource).locations(migrateLocation).baselineOnMigrate(true).load();
|
||||
if(!IsEmbedDB && (flyway.info().current() == null || flyway.info().current().getVersion().getMajor().intValue() < 43)) {
|
||||
System.out.println("数据库升级需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
|
||||
System.out.println("数据库执行初始化或升级需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
|
||||
}
|
||||
flyway.migrate();
|
||||
|
||||
|
||||
@@ -31,11 +31,12 @@ public class SensitiveFilter {
|
||||
return sensitiveWords;
|
||||
}
|
||||
|
||||
String originalText = text;
|
||||
text = text.toLowerCase();
|
||||
for(int i=0;i<text.length();i++){
|
||||
int sensitiveWordLength = getSensitiveWordLength(text, i, matchType);
|
||||
if(sensitiveWordLength>0){
|
||||
String sensitiveWord = text.substring(i, i + sensitiveWordLength);
|
||||
String sensitiveWord = originalText.substring(i, i + sensitiveWordLength);
|
||||
sensitiveWords.add(sensitiveWord);
|
||||
if(matchType==MatchType.MIN_MATCH){
|
||||
break;
|
||||
@@ -93,7 +94,7 @@ public class SensitiveFilter {
|
||||
Iterator<String> iterator = sensitiveWords.iterator();
|
||||
while (iterator.hasNext()){
|
||||
currentWord=iterator.next();
|
||||
if(currentWord==null||currentWord.trim().length()<2){ //敏感词长度必须大于等于2
|
||||
if(currentWord==null||currentWord.trim().length()<1){ //敏感词长度必须大于等于1
|
||||
continue;
|
||||
}
|
||||
currentMap=sensitiveWordsMap;
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.67</version>
|
||||
<version>0.70</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public interface APIPath {
|
||||
String Blacklist_Get_List = "/admin/blacklist/list";
|
||||
String Friend_Get_Alias = "/admin/friend/get_alias";
|
||||
String Friend_Set_Alias = "/admin/friend/set_alias";
|
||||
String Friend_Set_Extra = "/admin/friend/set_extra";
|
||||
String Friend_Send_Request = "/admin/friend/send_request";
|
||||
String Friend_Get_Requests = "/admin/friend/get_requests";
|
||||
String Relation_Get = "/admin/relation/get";
|
||||
@@ -71,6 +72,8 @@ public interface APIPath {
|
||||
String Get_System_Setting = "/admin/system/get_setting";
|
||||
String Put_System_Setting = "/admin/system/put_setting";
|
||||
|
||||
String Health = "/admin/health";
|
||||
|
||||
String Conference_List = "/admin/conference/list";
|
||||
String Conference_List_Participant = "/admin/conference/list_participant";
|
||||
String Conference_Create = "/admin/conference/create";
|
||||
|
||||
@@ -44,6 +44,7 @@ public enum ErrorCode {
|
||||
ERROR_CODE_RECALL_TIME_EXPIRED(24, "time expired"),
|
||||
ERROR_CODE_CONFERENCE_ROOM_NOT_EXIST(60, "conference room not exist"),
|
||||
|
||||
ERROR_CODE_USER_NOT_PREPARED(238, "user not prepared"),
|
||||
ERROR_CODE_API_NOT_SIGNED(239, "api not signed or sign parameter not completion"),
|
||||
ERROR_CODE_GROUP_EXCEED_MAX_MEMBER_COUNT(240, "group exceed max member count"),
|
||||
ERROR_CODE_GROUP_MUTED(241, "group is muted"),
|
||||
|
||||
@@ -11,6 +11,7 @@ public class IMExceptionEvent {
|
||||
int ROBOT_API_Exception = 7;
|
||||
int SHORT_LINK_Exception = 8;
|
||||
int EVENT_CALLBACK_Exception = 9;
|
||||
int CONFERENCE_Exception = 10;
|
||||
int HEART_BEAT = 100;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.wildfirechat.pojos;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HealthCheckResult extends ArrayList<HealthCheckResult.Node>
|
||||
{
|
||||
public static class Node {
|
||||
public String node;
|
||||
public CPU cpu;
|
||||
public Memory memory;
|
||||
public Disk disk;
|
||||
}
|
||||
|
||||
public static class Memory {
|
||||
public long avail;
|
||||
public long max;
|
||||
public long free;
|
||||
}
|
||||
public static class Disk {
|
||||
public long usable;
|
||||
public long free;
|
||||
public long space;
|
||||
}
|
||||
public static class CPU {
|
||||
public int cores;
|
||||
public double load;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package cn.wildfirechat.pojos;
|
||||
|
||||
|
||||
public class InputUpdateFriendExtra {
|
||||
private String operator;
|
||||
private String targetId;
|
||||
private String extra;
|
||||
|
||||
public String getTargetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
public void setTargetId(String targetId) {
|
||||
this.targetId = targetId;
|
||||
}
|
||||
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public void setOperator(String operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.wildfirechat.pojos;
|
||||
|
||||
public class PojoConferenceRoomId {
|
||||
public String roomId;
|
||||
public boolean advance;
|
||||
|
||||
public PojoConferenceRoomId() {
|
||||
}
|
||||
|
||||
public PojoConferenceRoomId(String roomId, boolean advance) {
|
||||
this.roomId = roomId;
|
||||
this.advance = advance;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
<!--<relativePath>../pom.xml</relativePath>-->
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.67</version>
|
||||
<version>0.70</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>distribution</artifactId>
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
</join>
|
||||
</network>
|
||||
|
||||
<properties>
|
||||
<property name="hazelcast.logging.type">slf4j</property>
|
||||
</properties>
|
||||
|
||||
<!-- map eviction -->
|
||||
<!-- http://docs.hazelcast.org/docs/latest-development/manual/html/Distributed_Data_Structures/Map/Map_Eviction.html -->
|
||||
<map name="messages_map">
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#*********************************************************************
|
||||
#服务器的接入IP。给客户端提供是${server.ip}和${http_port}。
|
||||
#客户端会从${http_port}端口获取到长链接端口。这个地址一定要改成客户端可以访问到的IP地址
|
||||
# (如果您部署云服务器上或者具有独立公网出口的服务器上,请改为对应的公网IP;如果您部署在在内网环境下,在内网使用,这个地方改成内网地址)
|
||||
#(如果您部署云服务器上或者具有独立公网出口的服务器上,请改为对应的公网IP;如果您部署在在内网环境下,在内网使用,这个地方改成内网地址)
|
||||
server.ip 0.0.0.0
|
||||
|
||||
##原生客户端长链接端口。如果使用4层的反向代理或者类似组件,需要设置长链接保持时间为10分钟。
|
||||
port 1883
|
||||
|
||||
##客户端短链接端口
|
||||
##客户端短链接端口,客户端固定使用80端口,不要修改此端口。
|
||||
http_port 80
|
||||
|
||||
#管理端口
|
||||
@@ -28,15 +28,15 @@ host 0.0.0.0
|
||||
##本地绑定端口
|
||||
local_port 80
|
||||
|
||||
##是否使用内置DB。0使用mysql;1使用h2db;2使用mysql+mongodb。社区版只支持0和1,专业版支持2
|
||||
##是否使用内置DB。0使用mysql;1使用h2db。
|
||||
embed.db 1
|
||||
|
||||
## 是否自动清理历史消息记录,当为true时,IM服务会定时扫描t_messages_x和t_user_messages_x表,清除掉3年前的历史消息。
|
||||
## 如果不开启自动清理,需要手动清理t_messages_x和t_user_messages_x表内的历史数据,保存数据不能超过3年。
|
||||
## 使用mongodb时次开关无效(因为mongodb设置了消息过期时间,会自动清理)。
|
||||
## 使用mongodb时次开关无效(因为mongodb设置了消息过期时间,会自动清理,仅专业版支持mongodb)。
|
||||
db.auto_clean_history_messages true
|
||||
|
||||
##服务器管理接口密钥
|
||||
##服务器管理接口密钥。如果修改此端口需要同步修改使用管理API的地方,比如应用服务。
|
||||
http.admin.secret_key 123456
|
||||
|
||||
##服务器API接口参数是否检查时间。当设置为false时,所有的请求会检查时间的有效性;当设置为true时,可以在http.admin.secret_key保持不变的情况下,使用固定的服务API签名
|
||||
@@ -69,14 +69,21 @@ message.blacklist.strategy 0
|
||||
##是否禁止服务器端消息搜索,该功能暂未实现。目前的影响是如果打开,则存储消息时不单独保存_searchable_content字段
|
||||
message.disable_remote_search false
|
||||
|
||||
##允许客户端撤回用户自己发送的消息时限,单位是秒。群组管理员或群主撤回群成员消息、server api不受此限制,可以撤回任意时限内的消息
|
||||
##是否数据库中加密消息内容。注意这个开关在服务运行起来后不能改变了,避免读取数据库时无法恢复历史消息。
|
||||
##如果要避免数据库中明文存储消息内容,请打开这个开关和message.disable_remote_search开关
|
||||
message.encrypt_message_content false
|
||||
|
||||
##允许客户端撤回用户自己发送的消息时限,单位是秒。
|
||||
##Server API不受此限制,可以撤回任意时限内的消息
|
||||
##群组管理员或群主撤回群成员消息不受此限制,可以撤回任意时限内的消息。但管理员或群主撤回自己的消息还是需要准守此时间限制。
|
||||
##修改此参数需要同步修改客户端UI,客户端UI代码有判断撤回是否显示的逻辑。
|
||||
message.recall_time_limit 120
|
||||
|
||||
##禁止群主/群管理员撤回群内消息,关闭时群主/群管理员可以撤回任意时间段内群内成员的消息,群主/群管理员撤回自己发送的消息还是会受到message.recall_time_limit时间限制。
|
||||
##禁止群主/群管理员撤回群内消息,关闭时群主/群管理员可以撤回任意时间段内群内成员的消息。
|
||||
message.disable_group_manager_recall false
|
||||
|
||||
## 禁止客户端发送消息类型,消息类型以逗号分开,下面3个值为示例,可以删除。
|
||||
## 一般用于重要类型消息,比如交易、充值等,禁止客户端发送,仅限于服务器发送,提高安全性。
|
||||
##禁止客户端发送消息类型,消息类型以逗号分开,下面3个值为示例,可以删除。
|
||||
##一般用于重要类型消息,比如交易、充值等,禁止客户端发送,仅限于服务器发送,提高安全性。
|
||||
#message.forbidden_client_send_types 3999,4000,4001
|
||||
|
||||
## 当禁止发送消息时,允许例外发送的消息。这种一般用户需要被动回应的消息。比如群中被禁言的用户,他无法主动发起音视频,但可以接听未被禁言用户的音视频
|
||||
@@ -108,7 +115,7 @@ friend.request_expiration_duration 604800000
|
||||
##添加好友后会发送欢迎消息,比如"你们已经是好友了,可以聊天了"。这个开关控制是用旧的方式还是用新的方式。
|
||||
##旧的方式会发送notification消息(contenttype是80),内容会根据好友接收者的语言选择对应的语言,如果双方语言不一致会导致另外一方看到的不是本地语言。
|
||||
##新的方式会发送添加好友消息(contenttype是93)和欢迎消息(contenttype是92),需要客户端实现这两种消息。
|
||||
##如果客户端已经实现这两种消息可以打开这个开关
|
||||
##如果客户端已经实现这两种消息可以打开这个开关(客户端2020.6.30添加了此消息类型)。
|
||||
friend.new_welcome_message true
|
||||
|
||||
## 聊天室观众空闲退出时间,单位为毫秒,默认为15分钟,0为永远不退出
|
||||
@@ -146,7 +153,7 @@ hazelcast.configuration config/hazelcast.xml
|
||||
# Media server configuration
|
||||
#*********************************************************************
|
||||
##是否使用七牛云存储。1使用七牛;0使用内存文件服务器。默认的七牛账户信息不可用,请在七牛官网申请账户并配置
|
||||
## https://docs.wildfirechat.cn/server/media_server.html
|
||||
##配置使用七牛请参考:https://docs.wildfirechat.cn/server/media_server.html
|
||||
media.server.use_qiniu 0
|
||||
|
||||
##媒体类型分类
|
||||
@@ -193,11 +200,17 @@ local.media.storage.root ./media
|
||||
# 尽管内置文件存储可以使用,但是我们还是建议使用专业级别的对象存储服务,社区版可以使用七牛,专业版另外支持阿里云和也是私有对象存储。详情请参考 https://docs.wildfirechat.cn/server/oss.html
|
||||
# local.media.storage.remote_server_url https://example.com/media
|
||||
|
||||
# 是否支持多端登陆,为true时支持任意平台任意多个客户端同时登录;为false时每个平台只支持一个端登录,但不同平台可以同时登录。
|
||||
# 是否支持任意多端登陆,为true时支持任意平台任意多个客户端同时登录;为false时每个平台只支持一个端登录,但不同平台可以同时登录。
|
||||
# Android/iOS为移动平台,windows/mac/linux为pc平台,web为web平台,小程序为小程序平台。
|
||||
# 建议使用false
|
||||
server.multi_endpoint false
|
||||
|
||||
# 多平台连接状态通知(仅当multi_endpoint为false时有效),true时移动端可以收到pc或web端登录的通知。
|
||||
server.multi_platform_notification false
|
||||
server.multi_platform_notification true
|
||||
|
||||
# 当pc或者web在线时,手机是否默认静音,默认为true。注意仅影响默认值,用户可以手动切换是否静音。
|
||||
# 如果这里改为false,客户端那边需要同步修改pc在线默认通知状态,方法是client的setDefaultSilentWhenPcOnline函数。
|
||||
server.mobile_default_silent_when_pc_online true
|
||||
|
||||
#*********************************************************************
|
||||
# Push server configuration
|
||||
@@ -214,6 +227,8 @@ push.ios.server.address http://localhost:8085/ios/push
|
||||
#*********************************************************************
|
||||
# 监控配置
|
||||
#*********************************************************************
|
||||
##异常事件产生回调
|
||||
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||
#monitor.exception_event_address http://localhost:8888/im_exception_event/
|
||||
|
||||
#*********************************************************************
|
||||
@@ -283,7 +298,7 @@ sensitive.filter.type 0
|
||||
#sensitive.remote_server_url http://192.168.3.202:8888/message/censor
|
||||
## 需要进行敏感词审核的消息类型,当有多个时以英文逗号分割
|
||||
#sensitive.remote_sensitive_message_type 1,2,3
|
||||
## 如果远程审核服务返回403,返回给发送端发送失败还是成功?实际上这个值影响发送响应是否等待审核结果,当为false时,消息发送给审核服务就立即返回
|
||||
## 成功给客户端。当为true时,消息发送给审核服务,等待审核结果,如果结果为继续发送就返回给客户端为成功,结果为禁止发送就返回失败给客户端。
|
||||
## 如果远程审核服务返回403,返回给发送端发送失败还是成功?实际上这个值影响发送响应是否等待审核结果,当为false时,消息发送给审核服务就立即返回成功给客户端。
|
||||
## 当为true时,消息发送给审核服务,等待审核结果,如果结果为继续发送就返回给客户端为成功,结果为禁止发送就返回失败给客户端。
|
||||
## 客户端操作的超时一般为10s左右,且考虑到复杂的网络情况,建议处理时间不要超过3s。建议这个值为false。
|
||||
#sensitive.remote_fail_when_matched false
|
||||
|
||||
@@ -72,18 +72,20 @@ JAVA_OPTS="$JAVA_OPTS -XX:MaxGCPauseMillis=500"
|
||||
|
||||
### GC logging options -- uncomment to enable
|
||||
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDateStamps"
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC"
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:+PrintTenuringDistribution"
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCApplicationStoppedTime"
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:+PrintPromotionFailure"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:+PrintTenuringDistribution"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCApplicationStoppedTime"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:+PrintPromotionFailure"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:PrintFLSStatistics=1"
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:+UseGCLogFileRotation"
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:NumberOfGCLogFiles=10"
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:+UseGCLogFileRotation"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:NumberOfGCLogFiles=10"
|
||||
#JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M"
|
||||
|
||||
echo ""
|
||||
echo "请设置JVM参数Xmx和Xms!!!"
|
||||
echo ""
|
||||
#JAVA_OPTS="$JAVA_OPTS -Xmx2G"
|
||||
#JAVA_OPTS="$JAVA_OPTS -Xms2G"
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
<version>0.67</version>
|
||||
<version>0.70</version>
|
||||
<name>Wildfire Chat</name>
|
||||
<description>Wildfire Chat Server</description>
|
||||
<inceptionYear>2019</inceptionYear>
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.67</version>
|
||||
<version>0.70</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ public class ConferenceAdmin {
|
||||
return AdminHttpUtils.httpJsonPost(path, null, PojoConferenceInfoList.class);
|
||||
}
|
||||
|
||||
public static IMResult<PojoConferenceParticipantList> listParticipants(String roomId) throws Exception {
|
||||
public static IMResult<PojoConferenceParticipantList> listParticipants(String roomId, boolean advance) throws Exception {
|
||||
String path = APIPath.Conference_List_Participant;
|
||||
return AdminHttpUtils.httpJsonPost(path, roomId, PojoConferenceParticipantList.class);
|
||||
PojoConferenceRoomId data = new PojoConferenceRoomId(roomId, advance);
|
||||
return AdminHttpUtils.httpJsonPost(path, data, PojoConferenceParticipantList.class);
|
||||
}
|
||||
|
||||
public static IMResult<Void> createRoom(String roomId, String description, String pin, int maxPublisher, boolean advance, int bitrate, boolean recording) throws Exception {
|
||||
@@ -29,8 +30,9 @@ public class ConferenceAdmin {
|
||||
return AdminHttpUtils.httpJsonPost(path, create, Void.class);
|
||||
}
|
||||
|
||||
public static IMResult<Void> destroy(String roomId) throws Exception {
|
||||
public static IMResult<Void> destroy(String roomId, boolean advance) throws Exception {
|
||||
String path = APIPath.Conference_Destroy;
|
||||
return AdminHttpUtils.httpJsonPost(path, roomId, Void.class);
|
||||
PojoConferenceRoomId conferenceRoomId = new PojoConferenceRoomId(roomId, advance);
|
||||
return AdminHttpUtils.httpJsonPost(path, conferenceRoomId, Void.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class GeneralAdmin {
|
||||
}
|
||||
|
||||
|
||||
private static IMResult<UserSettingPojo> getUserSetting(String userId, int scope, String key) throws Exception {
|
||||
public static IMResult<UserSettingPojo> getUserSetting(String userId, int scope, String key) throws Exception {
|
||||
String path = APIPath.User_Get_Setting;
|
||||
UserSettingPojo pojo = new UserSettingPojo();
|
||||
pojo.setUserId(userId);
|
||||
@@ -54,7 +54,7 @@ public class GeneralAdmin {
|
||||
return AdminHttpUtils.httpJsonPost(path, pojo, UserSettingPojo.class);
|
||||
}
|
||||
|
||||
private static IMResult<Void> setUserSetting(String userId, int scope, String key, String value) throws Exception {
|
||||
public static IMResult<Void> setUserSetting(String userId, int scope, String key, String value) throws Exception {
|
||||
String path = APIPath.User_Put_Setting;
|
||||
UserSettingPojo pojo = new UserSettingPojo();
|
||||
pojo.setUserId(userId);
|
||||
@@ -63,4 +63,8 @@ public class GeneralAdmin {
|
||||
pojo.setValue(value);
|
||||
return AdminHttpUtils.httpJsonPost(path, pojo, Void.class);
|
||||
}
|
||||
|
||||
public static IMResult<HealthCheckResult> healthCheck() throws Exception {
|
||||
return AdminHttpUtils.httpGet(APIPath.Health, HealthCheckResult.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,6 +381,15 @@ public class Main {
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
String friendExtra = "hello friend extra";
|
||||
IMResult<Void> setExtraResult = RelationAdmin.updateFriendExtra("ff1", "ff2", friendExtra);
|
||||
if (setExtraResult != null && setExtraResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("set friend extra success");
|
||||
} else {
|
||||
System.out.println("set friend extra failure");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
IMResult<RelationPojo> getRelation = RelationAdmin.getRelation("ff1", "ff2");
|
||||
if (getRelation != null && getRelation.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("get friend relation success");
|
||||
@@ -388,6 +397,11 @@ public class Main {
|
||||
System.out.println("get friend relation failure");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
if(!friendExtra.equals(getRelation.getResult().extra)) {
|
||||
System.out.println("set friend extra failure");
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
//***********************************************
|
||||
//**** 群组相关功能
|
||||
@@ -707,6 +721,14 @@ public class Main {
|
||||
System.out.println("create channel failure");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
IMResult<HealthCheckResult> health = GeneralAdmin.healthCheck();
|
||||
if(health != null && health.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println(health.result);
|
||||
} else {
|
||||
System.out.println("health check failure");
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void testChatroom() throws Exception {
|
||||
@@ -1302,6 +1324,8 @@ public class Main {
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
IMResult<InputOutputSensitiveWords> swResult = SensitiveAdmin.getSensitives();
|
||||
if (swResult != null && swResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS && swResult.getResult().getWords().containsAll(words)) {
|
||||
System.out.println("Sensitive word added");
|
||||
@@ -1318,6 +1342,7 @@ public class Main {
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
Thread.sleep(100);
|
||||
swResult = SensitiveAdmin.getSensitives();
|
||||
if (swResult != null && swResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS && !swResult.getResult().getWords().containsAll(words)) {
|
||||
System.out.println("Sensitive word removed");
|
||||
@@ -1384,7 +1409,7 @@ public class Main {
|
||||
}
|
||||
|
||||
for (PojoConferenceInfo conferenceInfo:listResult.getResult().conferenceInfoList) {
|
||||
IMResult<Void> destroyResult = ConferenceAdmin.destroy(conferenceInfo.roomId);
|
||||
IMResult<Void> destroyResult = ConferenceAdmin.destroy(conferenceInfo.roomId, conferenceInfo.advance);
|
||||
if(destroyResult == null || destroyResult.getErrorCode() != ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("destroy room failure");
|
||||
System.exit(-1);
|
||||
@@ -1401,9 +1426,17 @@ public class Main {
|
||||
System.out.println("conference list " + listResult.getResult().conferenceInfoList);
|
||||
}
|
||||
|
||||
IMResult<Void> createResult = ConferenceAdmin.createRoom("helloroomid", "hello room description", "123456", 20, false, 0, false);
|
||||
IMResult<Void> createResult = ConferenceAdmin.createRoom("helloroomid", "hello room description", "123456", 9, false, 0, false);
|
||||
if(createResult == null || createResult.getErrorCode() != ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("create conference list failure");
|
||||
System.out.println("create conference failure");
|
||||
System.exit(-1);
|
||||
} else {
|
||||
System.out.println("create conference");
|
||||
}
|
||||
|
||||
createResult = ConferenceAdmin.createRoom("helloroomid2", "hello room description advanced", "123456", 20, true, 0, false);
|
||||
if(createResult == null || createResult.getErrorCode() != ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("create conference failure");
|
||||
System.exit(-1);
|
||||
} else {
|
||||
System.out.println("create conference");
|
||||
@@ -1418,12 +1451,12 @@ public class Main {
|
||||
}
|
||||
|
||||
for (PojoConferenceInfo conferenceInfo:listResult.getResult().conferenceInfoList) {
|
||||
IMResult<PojoConferenceParticipantList> listParticipantsResult = ConferenceAdmin.listParticipants(conferenceInfo.roomId);
|
||||
IMResult<PojoConferenceParticipantList> listParticipantsResult = ConferenceAdmin.listParticipants(conferenceInfo.roomId, conferenceInfo.advance);
|
||||
if(listParticipantsResult == null || listParticipantsResult.getErrorCode() != ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
System.out.println("destroy room failure");
|
||||
System.out.println("list participants failure");
|
||||
System.exit(-1);
|
||||
} else {
|
||||
System.out.println("destroy room success");
|
||||
System.out.println("list participants success");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,14 @@ public class RelationAdmin {
|
||||
return AdminHttpUtils.httpJsonPost(path, input, OutputGetAlias.class);
|
||||
}
|
||||
|
||||
public static IMResult<Void> updateFriendExtra(String operator, String targetId, String extra) throws Exception {
|
||||
String path = APIPath.Friend_Set_Extra;
|
||||
InputUpdateFriendExtra input = new InputUpdateFriendExtra();
|
||||
input.setOperator(operator);
|
||||
input.setTargetId(targetId);
|
||||
input.setExtra(extra);
|
||||
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
|
||||
}
|
||||
public static IMResult<Void> sendFriendRequest(String userId, String targetId, String reason, boolean force) throws Exception {
|
||||
String path = APIPath.Friend_Send_Request;
|
||||
InputAddFriendRequest input = new InputAddFriendRequest();
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
@@ -34,7 +35,54 @@ public class AdminHttpUtils {
|
||||
httpClient = HttpClients.createDefault();
|
||||
}
|
||||
|
||||
public static <T> IMResult<T> httpJsonPost(String path, Object object, Class<T> clazz) throws Exception{
|
||||
public static <T> IMResult<T> httpGet(String path, Class<T> clazz) throws Exception {
|
||||
if (isNullOrEmpty(adminUrl) || isNullOrEmpty(adminSecret)) {
|
||||
LOG.error("野火IM Server SDK必须先初始化才能使用,是不是忘记初始化了!!!!");
|
||||
throw new Exception("SDK没有初始化");
|
||||
}
|
||||
|
||||
if (isNullOrEmpty(path)) {
|
||||
throw new Exception("路径缺失");
|
||||
}
|
||||
HttpGet get = null;
|
||||
try {
|
||||
get = new HttpGet(adminUrl + path);
|
||||
HttpResponse response = httpClient.execute(get);
|
||||
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
String content = null;
|
||||
if (response.getEntity().getContentLength() > 0) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "utf-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
String NL = System.getProperty("line.separator");
|
||||
while ((line = in.readLine()) != null) {
|
||||
sb.append(line).append(NL);
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
content = sb.toString();
|
||||
LOG.info("http request response content: {}", content);
|
||||
}
|
||||
|
||||
if(statusCode != HttpStatus.SC_OK){
|
||||
LOG.info("Request error: " + statusCode + " error msg:" + content);
|
||||
throw new Exception("Http request error with code:" + statusCode);
|
||||
} else {
|
||||
return fromJsonObject(content, clazz);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
} finally {
|
||||
if(get != null) {
|
||||
get.releaseConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> IMResult<T> httpJsonPost(String path, Object object, Class<T> clazz) throws Exception {
|
||||
if (isNullOrEmpty(adminUrl) || isNullOrEmpty(adminSecret)) {
|
||||
LOG.error("野火IM Server SDK必须先初始化才能使用,是不是忘记初始化了!!!!");
|
||||
throw new Exception("SDK没有初始化");
|
||||
|
||||
Reference in New Issue
Block a user