Compare commits

..
22 Commits
Author SHA1 Message Date
heavyrian2012 1486b19ce1 更新版本和release note 2022-11-19 09:44:09 +08:00
heavyrian2012 3e1095f745 移动release note文件的位置 2022-11-19 09:38:26 +08:00
heavyrian2012 17039bd3c1 http请求错误时不打印数据内容,防止内容泄露 2022-11-19 09:31:22 +08:00
heavyrian2012 b5d8feab62 客户端连接时检查是否跟获取token的平台一致 2022-11-18 22:10:53 +08:00
heavyrian2012 95b08ff7a3 Revert "mqtt proto version error"
This reverts commit ab8ae9d0c9.
2022-11-18 21:55:49 +08:00
heavyrian2012 ab8ae9d0c9 mqtt proto version error 2022-11-18 21:27:18 +08:00
heavyrian2012 b4d163e144 添加android客户端签名验证功能 2022-11-18 16:01:17 +08:00
heavyrian2012 3ccfd9a29b 解决user setting并发问题,fix #566 2022-11-15 17:12:51 +08:00
heavyrian2012 90504d1305 打包dockerfile 2022-11-14 15:47:42 +08:00
heavyrian2012 88199189a2 当禁止陌生人聊天时,添加例外允许用户id,用于比如管理员、文件助手等场景 2022-11-14 15:23:03 +08:00
heavyrian2012 0b6612eacc 内置对象存储支持绝对路径和相对路径 2022-11-14 15:13:06 +08:00
heavyrian2012 d706f545d2 解决启动脚本中的小错误 2022-11-14 15:04:51 +08:00
heavyrian2012 ae8d63b162 update docker readme 2022-11-14 14:42:04 +08:00
heavyrian2012 77e742e812 添加server sdk检查server api的工具 2022-11-12 19:25:24 +08:00
heavyrian2012 b2ee8b8d94 linux安装包创建日志目录 2022-11-12 19:24:57 +08:00
heavyrian2012 7080d85605 只有server api才能创建组织群 2022-11-12 15:01:57 +08:00
heavyrian2012 1275b2109f fix build error 2022-11-12 14:18:26 +08:00
heavyrian2012 b2eab28cb9 组织群客户端不能自主退出 2022-11-12 14:17:15 +08:00
heavyrian2012 67234ceaa6 添加新的群组类型:组织群 2022-11-12 14:00:24 +08:00
heavyrian2012 45f9e20e97 支持转发消息include/exclude类型,支持配置消息区间 2022-11-12 12:51:25 +08:00
heavyrian2012 6b7edc713a API获取用户信息时,过滤掉被删除的用户 2022-11-09 10:36:52 +08:00
heavyrian2012 1ef8f94d67 忽略掉无效的请求和日志 2022-11-05 17:05:31 +08:00
36 changed files with 427 additions and 92 deletions
+15 -3
View File
@@ -59,6 +59,12 @@ token.key testim
##token的过期时间,单位为毫秒,默认为无限期。如果需要设置无限期,客户端上一定需要加上token过期的处理。token过期的处理请参考文档的常见问题
##token.expire_time 2592000000
##客户端(目前只有android)的应用签名(一个应用最取第一个签名),如果有多个签名(多个客户端),用英文逗号分开。获取签名方法请参考文档:https://docs.wildfirechat.cn/blogs/签名验证.html
#connect.client_signature_list eNkezQ1g9OsnhfLSFUY1vzKDzhs=,xykezQ1g9OsnhfLSFUY1vzKDzhs=
##是否拒绝空签名的android客户端, 默认为true。当有旧的客户端需要兼容时改成false,当所有都是新客户端时,改成true。
#connect.reject_empty_signature true
##首次登录,是否接收之前的历史消息。
##0 不接收历史消息,只接收${message.compensate_time_limit}毫秒以内的消息,由于服务器没有保存已经收取记录,所以如果有超过${message.compensate_time_limit}毫秒之前未收取的消息也不会收取下拉;
##1接收,会接收 message.max_queue 配置的条数的历史消息
@@ -79,6 +85,9 @@ message.chatroom_remote_history_message 1
##是否禁止陌生人聊天
message.disable_stranger_chat false
##当禁止陌生人聊天时,允许聊天的用户id,比如管理员或者文件传输助手等。用户id以英文逗号分割。
message.allow_stranger_chat_list admin,FireRobot,wfc_file_transfer
##黑名单策略,0 发送失败,返回被拉黑的错误码;1 发送成功但消息被服务器直接丢弃
message.blacklist.strategy 0
@@ -293,14 +302,17 @@ push.ios.server.address http://localhost:8085/ios/push
##消息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.forward.url http://localhost:8888/im_event/message
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3
##需要转发的消息类型,当有多个时以英文逗号分割,可以用-来指定区间,注意区间不能太大,因为要把区间内的所有数字都放入到Set中。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3,5-10
##需要转发的消息类型,当有多个时以英文逗号分割,可以用-来指定区间,注意区间不能太大,因为要把区间内的所有数字都放入到Set中。
#message.forward.exclude_types 1,2,3,5-10
##敏感消息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.sensitive.forward.url http://localhost:8888/im_event/message
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。
##此转发不受${message.forward.types}和${message.forward.exclude_types}限制。
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
+1 -1
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.98</version>
<version>0.99</version>
</parent>
<artifactId>moquette-broker</artifactId>
@@ -240,7 +240,12 @@ public class UploadFileAction extends Action {
datePath = "fs/" + bucket[0] + "/" + datePath; //add bucket
String dir = "./" + MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
String dir;
if(MediaServerConfig.FILE_STROAGE_ROOT.startsWith("/") || MediaServerConfig.FILE_STROAGE_ROOT.startsWith("~/") || MediaServerConfig.FILE_STROAGE_ROOT.startsWith("./")) {
dir = MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
} else {
dir = "./" + MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
}
File dirFile = new File(dir);
boolean bFile = dirFile.exists();
@@ -49,7 +49,7 @@ public class GetUserAction extends AdminAction {
}
RestResult result;
if (user == null) {
if (user == null || user.getDeleted() > 0) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
result = RestResult.ok(InputOutputUserInfo.fromPbUser(user));
@@ -44,7 +44,7 @@ public class GetUserAction extends ChannelAction {
RestResult result;
if (user == null || StringUtil.isNullOrEmpty(user.getName())) {
if (user == null || StringUtil.isNullOrEmpty(user.getName()) || user.getDeleted() > 0) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
if (channelInfo.getOwner().equals(inputUserId.getUserId()) ||
@@ -44,7 +44,7 @@ public class GetUserAction extends RobotAction {
}
RestResult result;
if (user == null) {
if (user == null || user.getDeleted() > 0) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
result = RestResult.ok(InputOutputUserInfo.fromPbUser(user));
@@ -34,7 +34,6 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {
Logger.info("Http request whit url {}", fullHttpRequest.uri());
final Request request = Request.build(ctx, fullHttpRequest);
final Response response = Response.build(ctx, request);
@@ -124,6 +123,9 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
Action action;
if (isValidePath(request.getPath())) {
action = ServerSetting.getAction(request.getPath(), request.getMethod().toUpperCase());
if (action != null) {
Logger.info("Http request whit url {}", request.getPath());
}
} else {
action = ServerSetting.getErrorAction(ServerSetting.MAPPING_ERROR);
}
@@ -137,7 +139,11 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
action = ServerSetting.getAction(ServerSetting.MAPPING_ALL, request.getMethod());
if(null == action){
// 非Action方法,调用静态文件读取
action = Singleton.get(FileAction.class);
if(request.getPath().startsWith("/fs")) {
action = Singleton.get(FileAction.class);
} else {
action = ServerSetting.getErrorAction(ServerSetting.MAPPING_ERROR);
}
}
}
action.ctx = ctx;
@@ -110,6 +110,9 @@ public final class BrokerConstants {
public static final String TOKEN_SECRET_KEY = "token.key";
public static final String TOKEN_EXPIRE_TIME = "token.expire_time";
public static final String CONNECT_CLIENT_SIGNATURE_LIST = "connect.client_signature_list";
public static final String CONNECT_REJECT_EMPTY_SIGNATURE = "connect.reject_empty_signature";
public static final String EMBED_DB_PROPERTY_NAME = "embed.db";
public static final String DB_AUTO_CLEAN_HISTORY_MESSAGES = "db.auto_clean_history_messages";
public static final String H2DB_PATH = "h2db.path";
@@ -124,6 +127,7 @@ public final class BrokerConstants {
public static final String MESSAGE_Forward_Url = "message.forward.url";
public static final String MESSAGE_Sensitive_Forward_Url = "message.sensitive.forward.url";
public static final String MESSAGE_Forward_Types = "message.forward.types";
public static final String MESSAGE_Forward_Exclude_Types = "message.forward.exclude_types";
public static final String MESSAGE_MentionMsg_Forward_Url = "message.mentionmsg.forward.url";
@@ -141,6 +145,7 @@ public final class BrokerConstants {
public static final String MESSAGE_Max_Queue = "message.max_queue";
public static final String MESSAGE_Disable_Stranger_Chat = "message.disable_stranger_chat";
public static final String MESSAGE_Allow_Stranger_Chat_List = "message.allow_stranger_chat_list";
public static final String MESSAGE_Blacklist_Strategy = "message.blacklist.strategy";
@@ -40,6 +40,10 @@ public class CreateGroupHandler extends GroupHandler<WFCMessage.CreateGroupReque
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(!isAdmin && request.getGroup().getGroupInfo().getType() == ProtoConstants.GroupType.GroupType_Organization) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
WFCMessage.GroupInfo groupInfo = m_messagesStore.createGroup(fromUser, request.getGroup().getGroupInfo(), request.getGroup().getMembersList(), request.getMemberExtra(), isAdmin);
if (groupInfo != null) {
if(request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
@@ -29,6 +29,10 @@ public class KickoffGroupMember extends GroupHandler<WFCMessage.RemoveGroupMembe
errorCode = ErrorCode.ERROR_CODE_NOT_EXIST;
} else {
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
if (!isAdmin && groupInfo.getType() == ProtoConstants.GroupType.GroupType_Organization) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
boolean isAllow = isAdmin;
if (!isAllow) {
if (groupInfo.getOwner() != null) {
@@ -31,7 +31,7 @@ public class QuitGroupHandler extends GroupHandler<WFCMessage.QuitGroupRequest>
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
ErrorCode errorCode = m_messagesStore.quitGroup(fromUser, request.getGroupId());
ErrorCode errorCode = m_messagesStore.quitGroup(fromUser, request.getGroupId(), isAdmin);
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), request.getNotifyContent());
@@ -40,6 +40,11 @@ public class RouteHandler extends IMHandler<WFCMessage.RouteRequest> {
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
}
if(request.getPlatform() != session.getPlatform() && m_messagesStore.existSignatures()) {
LOG.error("Session <{}, {}> platform is {} mismatch the request {}", session.getUsername(), session.getClientID(), session.getPlatform(), request.getPlatform());
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
}
String serverIp = mServer.getServerIp();
String longPort = mServer.getLongPort();
String shortPort = mServer.getShortPort();
@@ -22,6 +22,7 @@ import cn.wildfirechat.common.ErrorCode;
import io.netty.buffer.Unpooled;
import win.liyufan.im.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -33,6 +34,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
private String mForwardUrl = null;
private String mSensitiveMessageForwardUrl = null;
private Set<Integer> mForwardMessageTypes = new HashSet<>();
private Set<Integer> mForwardExcludeMessageTypes = new HashSet<>();
private String mMentionForwardUrl = null;
private int mBlacklistStrategy = 0; //黑名单中时,0失败,1吞掉。
private boolean mNoForwardAdminMessage = false;
@@ -47,12 +49,9 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
mForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url);
if (!StringUtil.isNullOrEmpty(mForwardUrl)) {
String forwardTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Types);
if (!StringUtil.isNullOrEmpty(forwardTypes)) {
String[] tss = forwardTypes.split(",");
for (String ts:tss) {
mForwardMessageTypes.add(Integer.parseInt(ts.trim()));
}
}
parseTypes(mForwardMessageTypes, forwardTypes);
String excludeTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Exclude_Types);
parseTypes(mForwardExcludeMessageTypes, excludeTypes);
}
mSensitiveMessageForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Sensitive_Forward_Url);
mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url);
@@ -97,6 +96,30 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
private void parseTypes(Collection<Integer> collection, String types) {
try {
if (!StringUtil.isNullOrEmpty(types)) {
String[] tss = types.split(",");
for (String ts:tss) {
if(ts.contains("-")) {
String[] ss = ts.split("-");
if(ss.length == 2) {
int begin = Integer.parseInt(ss[0]);
int end = Integer.parseInt(ss[1]);
for (int i = begin; i <= end ; i++) {
collection.add(i);
}
}
} else {
collection.add(Integer.parseInt(ts.trim()));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.Message message, Qos1PublishHandler.IMCallback callback) {
ErrorCode errorCode = ErrorCode.ERROR_CODE_SUCCESS;
@@ -179,7 +202,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
}
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage)) {
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage) && !mForwardExcludeMessageTypes.contains(message.getContent().getType())) {
publisher.forwardMessage(message, mForwardUrl, outputClient);
}
@@ -49,6 +49,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
@@ -121,6 +122,8 @@ public class MemoryMessagesStore implements IMessagesStore {
private Lock mReadLock = mLock.readLock();
private Lock mWriteLock = mLock.writeLock();
private Lock mUserSettingLock = new ReentrantLock();
private final DatabaseStore databaseStore;
private ConcurrentHashMap<String, Long> userMaxPullSeq = new ConcurrentHashMap<>();
@@ -142,6 +145,10 @@ public class MemoryMessagesStore implements IMessagesStore {
private boolean mMultiPlatformNotification = false;
private boolean mMobileDefaultSilentWhenPCOnline = true;
private boolean mDisableStrangerChat = false;
private Set<String> mAllowStrangerChatSet = new HashSet<>();
private Set<String> mClientSignatureSet = new HashSet<>();
private boolean mRejectEmptySignature = true;
private long mChatroomParticipantIdleTime = 900000;
private boolean mChatroomRejoinWhenActive = true;
@@ -187,6 +194,27 @@ public class MemoryMessagesStore implements IMessagesStore {
IS_CHATROOM_MESSAGE_REMOTE_HISTORY_MESSAGE = "1".equals(m_Server.getConfig().getProperty(MESSAGE_Remote_Chatroom_History_Message, "1"));
Constants.MAX_MESSAGE_QUEUE = Integer.parseInt(m_Server.getConfig().getProperty(MESSAGE_Max_Queue));
try {
String signatureList = m_Server.getConfig().getProperty(BrokerConstants.CONNECT_CLIENT_SIGNATURE_LIST);
if(!StringUtil.isNullOrEmptyAfterTrim(signatureList)) {
signatureList = signatureList.replace(",", ",");
for (String s : signatureList.split(",")) {
mClientSignatureSet.add(s.trim());
}
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if(!mClientSignatureSet.isEmpty()) {
mRejectEmptySignature = Boolean.parseBoolean(m_Server.getConfig().getProperty(BrokerConstants.CONNECT_REJECT_EMPTY_SIGNATURE, "true"));
} else {
mRejectEmptySignature = false;
}
} catch (Exception e) {
}
try {
mDisableStrangerChat = Boolean.parseBoolean(m_Server.getConfig().getProperty(BrokerConstants.MESSAGE_Disable_Stranger_Chat));
} catch (Exception e) {
@@ -195,6 +223,18 @@ public class MemoryMessagesStore implements IMessagesStore {
printMissConfigLog(MESSAGE_Disable_Stranger_Chat, mDisableStrangerChat + "");
}
try {
String allowStrangerList = m_Server.getConfig().getProperty(BrokerConstants.MESSAGE_Allow_Stranger_Chat_List);
if(!StringUtil.isNullOrEmptyAfterTrim(allowStrangerList)) {
allowStrangerList = allowStrangerList.replace(",", ",");
for (String s : allowStrangerList.split(",")) {
mAllowStrangerChatSet.add(s.trim());
}
}
} catch (Exception e) {
e.printStackTrace();
}
try {
mMultiPlatformNotification = Boolean.parseBoolean(m_Server.getConfig().getProperty(BrokerConstants.SERVER_MULTI_PLATFROM_NOTIFICATION, "false"));
} catch (Exception e) {
@@ -1266,6 +1306,9 @@ public class MemoryMessagesStore implements IMessagesStore {
int maxCount = Integer.MAX_VALUE;
if (!isAdmin) {
if (groupInfo.getType() == ProtoConstants.GroupType.GroupType_Organization) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
boolean isMember = false;
boolean isManager = false;
WFCMessage.GroupMember gm = getGroupMember(groupId, operator);
@@ -1406,18 +1449,22 @@ public class MemoryMessagesStore implements IMessagesStore {
void removeFavGroup(String groupId, List<String> memberIds) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
databaseStore.removeFavGroup(groupId, memberIds);
for (String member : memberIds) {
userSettingMap.remove(member);
try {
mUserSettingLock.lock();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
for (String member : memberIds) {
userSettingMap.remove(member);
}
} finally {
mUserSettingLock.unlock();
}
}
@Override
public ErrorCode quitGroup(String operator, String groupId) {
public ErrorCode quitGroup(String operator, String groupId, boolean admin) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
IMap<String, WFCMessage.GroupInfo> mIMap = hzInstance.getMap(GROUPS_MAP);
@@ -1437,6 +1484,10 @@ public class MemoryMessagesStore implements IMessagesStore {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
if(!admin && groupInfo.getType() == ProtoConstants.GroupType.GroupType_Organization) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if (groupInfo.getType() != ProtoConstants.GroupType.GroupType_Free && groupInfo.getOwner() != null && groupInfo.getOwner().equals(operator)) {
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
@@ -1479,7 +1530,7 @@ public class MemoryMessagesStore implements IMessagesStore {
int[] removedCount = new int[1];
for (String groupId : groupIds) {
removedCount[0] = 0;
ErrorCode errorCode = quitGroup(userId, groupId);
ErrorCode errorCode = quitGroup(userId, groupId, true);
LOG.info("clear user {} group {} result {}", userId, groupId, errorCode);
}
}
@@ -1487,13 +1538,17 @@ public class MemoryMessagesStore implements IMessagesStore {
private void removeGroupUserSettings(String groupId, List<String> users) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
databaseStore.removeGroupUserSettings(groupId, users);
for (String userId:users) {
userSettingMap.remove(userId);
try {
mUserSettingLock.lock();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
for (String userId:users) {
userSettingMap.remove(userId);
}
} finally {
mUserSettingLock.unlock();
}
}
@Override
@@ -1511,6 +1566,7 @@ public class MemoryMessagesStore implements IMessagesStore {
}
if (!isAdmin && (groupInfo.getType() == ProtoConstants.GroupType.GroupType_Free ||
groupInfo.getType() == ProtoConstants.GroupType.GroupType_Organization ||
(groupInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted || groupInfo.getType() == ProtoConstants.GroupType.GroupType_Normal)
&& (groupInfo.getOwner() == null || !groupInfo.getOwner().equals(operator)))) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
@@ -1554,7 +1610,11 @@ public class MemoryMessagesStore implements IMessagesStore {
}
if (!isAdmin) {
if (oldInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted) {
if (oldInfo.getType() == ProtoConstants.GroupType.GroupType_Organization && modifyType != Modify_Group_Extra && modifyType != Modify_Group_Mute && modifyType != Modify_Group_PrivateChat) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if (oldInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted || oldInfo.getType() == ProtoConstants.GroupType.GroupType_Organization) {
boolean isAllow = false;
if ((oldInfo.getOwner() != null && oldInfo.getOwner().equals(operator))) {
isAllow = true;
@@ -1920,6 +1980,10 @@ public class MemoryMessagesStore implements IMessagesStore {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if (!isAdmin && groupInfo.getType() == ProtoConstants.GroupType.GroupType_Organization) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
//check the new owner is in member list? is that necessary?
long updateDt = System.currentTimeMillis();
groupInfo = groupInfo.toBuilder().setOwner(newOwner).setUpdateDt(updateDt).setMemberUpdateDt(updateDt).build();
@@ -2784,6 +2848,11 @@ public class MemoryMessagesStore implements IMessagesStore {
if (mDisableStrangerChat) {
//在禁止私聊时,是否是允许私聊的用户id
if(mAllowStrangerChatSet.contains(targetUser)) {
return ErrorCode.ERROR_CODE_SUCCESS;
}
//在禁止私聊时,允许机器人,物联网设备及管理员进行私聊。
IMap<String, WFCMessage.User> mUserMap = hzInstance.getMap(USERS);
WFCMessage.User target = mUserMap.get(targetUser);
@@ -3553,9 +3622,15 @@ public class MemoryMessagesStore implements IMessagesStore {
IMap<String, WFCMessage.GroupInfo> mGroups = hzInstance.getMap(GROUPS_MAP);
IMap<String, WFCMessage.ChannelInfo> mChannels = hzInstance.getMap(CHANNELS);
Collection<WFCMessage.UserSettingEntry> entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
Collection<WFCMessage.UserSettingEntry> entries;
try {
mUserSettingLock.lock();
entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
}
} finally {
mUserSettingLock.unlock();
}
ErrorCode ec = ErrorCode.ERROR_CODE_NOT_MODIFIED;
@@ -3634,17 +3709,22 @@ public class MemoryMessagesStore implements IMessagesStore {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
Collection<WFCMessage.UserSettingEntry> entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
}
try {
mUserSettingLock.lock();
Collection<WFCMessage.UserSettingEntry> entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
}
if (entries != null) {
for (WFCMessage.UserSettingEntry entry : entries) {
if (entry.getScope() == scope && (key== null || key.equals(entry.getKey()))) {
return entry;
if (entries != null) {
for (WFCMessage.UserSettingEntry entry : entries) {
if (entry.getScope() == scope && (key== null || key.equals(entry.getKey()))) {
return entry;
}
}
}
} finally {
mUserSettingLock.unlock();
}
return null;
@@ -3655,19 +3735,25 @@ public class MemoryMessagesStore implements IMessagesStore {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
Collection<WFCMessage.UserSettingEntry> entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
}
List<WFCMessage.UserSettingEntry> result;
try {
mUserSettingLock.lock();
Collection<WFCMessage.UserSettingEntry> entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
}
List<WFCMessage.UserSettingEntry> result = new ArrayList<>();
if (entries != null) {
for (WFCMessage.UserSettingEntry entry : entries
) {
if (entry.getScope() == scope) {
result.add(entry);
result = new ArrayList<>();
if (entries != null) {
for (WFCMessage.UserSettingEntry entry : entries
) {
if (entry.getScope() == scope) {
result.add(entry);
}
}
}
} finally {
mUserSettingLock.unlock();
}
return result;
@@ -3678,28 +3764,34 @@ public class MemoryMessagesStore implements IMessagesStore {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
Collection<WFCMessage.UserSettingEntry> entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
if (entries == null) {
entries = new ArrayList<>();
long updateDt = 0;
try {
mUserSettingLock.lock();
Collection<WFCMessage.UserSettingEntry> entries = userSettingMap.get(userId);
if (entries == null || entries.size() == 0) {
entries = loadPersistedUserSettings(userId, userSettingMap);
if (entries == null) {
entries = new ArrayList<>();
}
}
}
long updateDt = System.currentTimeMillis();
WFCMessage.UserSettingEntry settingEntry = WFCMessage.UserSettingEntry.newBuilder().setScope(request.getScope()).setKey(request.getKey()).setValue(request.getValue()).setUpdateDt(updateDt).build();
databaseStore.persistUserSetting(userId, settingEntry);
updateDt = System.currentTimeMillis();
WFCMessage.UserSettingEntry settingEntry = WFCMessage.UserSettingEntry.newBuilder().setScope(request.getScope()).setKey(request.getKey()).setValue(request.getValue()).setUpdateDt(updateDt).build();
databaseStore.persistUserSetting(userId, settingEntry);
for (WFCMessage.UserSettingEntry entry : entries
) {
if (entry.getScope() == request.getScope() && entry.getKey().equals(request.getKey())) {
userSettingMap.remove(userId, entry);
userSettingMap.put(userId, settingEntry);
break;
for (WFCMessage.UserSettingEntry entry : entries
) {
if (entry.getScope() == request.getScope() && entry.getKey().equals(request.getKey())) {
userSettingMap.remove(userId, entry);
userSettingMap.put(userId, settingEntry);
break;
}
}
}
userSettingMap.put(userId, settingEntry);
userSettingMap.put(userId, settingEntry);
} finally {
mUserSettingLock.unlock();
}
if(request.getScope() == UserSettingScope.kUserSettingConversationSilent) {
int firstSplit = request.getKey().indexOf("-");
@@ -3724,8 +3816,14 @@ public class MemoryMessagesStore implements IMessagesStore {
public void clearUserSettings(String userId) {
databaseStore.clearUserSetting(userId);
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
userSettingMap.remove(userId);
try {
mUserSettingLock.lock();
MultiMap<String, WFCMessage.UserSettingEntry> userSettingMap = hzInstance.getMultiMap(USER_SETTING);
userSettingMap.remove(userId);
} finally {
mUserSettingLock.unlock();
}
}
@Override
@@ -4256,6 +4354,34 @@ public class MemoryMessagesStore implements IMessagesStore {
return UUIDGenerator.getUUID();
}
@Override
public boolean checkSignature(String signature) {
if(mClientSignatureSet.isEmpty()) {
return true;
}
if(StringUtil.isNullOrEmptyAfterTrim(signature)) {
if(mRejectEmptySignature) {
LOG.info("Failed check signature, no signature");
return false;
} else {
return true;
}
} else {
if(mClientSignatureSet.contains(signature)) {
return true;
} else {
LOG.info("Failed check signature, signature no matched");
return false;
}
}
}
@Override
public boolean existSignatures() {
return !mClientSignatureSet.isEmpty();
}
@Override
public void storeRetained(Topic topic, StoredMessage storedMessage) {
LOG.debug("Store retained message for topic={}, CId={}", topic, storedMessage.getClientID());
@@ -110,7 +110,7 @@ public interface IMessagesStore {
WFCMessage.GroupInfo createGroup(String operator, WFCMessage.GroupInfo groupInfo, List<WFCMessage.GroupMember> memberList, String memberExtra, boolean isAdmin);
ErrorCode addGroupMembers(String operator, boolean isAdmin, String groupId, List<WFCMessage.GroupMember> memberList, String extra);
ErrorCode kickoffGroupMembers(String operator, boolean isAdmin, String groupId, List<String> memberList);
ErrorCode quitGroup(String operator, String groupId);
ErrorCode quitGroup(String operator, String groupId, boolean admin);
void clearUserGroups(String userId);
ErrorCode dismissGroup(String operator, String groupId, boolean isAdmin);
ErrorCode modifyGroupInfo(String operator, String groupId, int modifyType, String value, boolean isAdmin);
@@ -249,6 +249,9 @@ public interface IMessagesStore {
//使用了数据库,会比较慢,仅能用户用户/群组等id的生成
String getShortUUID();
boolean checkSignature(String signature);
boolean existSignatures();
/**
* Used to initialize all persistent store structures
*/
@@ -48,6 +48,7 @@ import win.liyufan.im.HttpUtils;
import win.liyufan.im.IMTopic;
import win.liyufan.im.Utility;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -339,6 +340,24 @@ public class ProtocolProcessor {
failedCredentials(channel);
return false;
}
if(session.getPlatform() == ProtoConstants.Platform.Platform_Android || session.getPlatform() == ProtoConstants.Platform.Platform_APad) {
byte[] signature = msg.payload().signatureInBytes();
String basedSign = null;
if (signature != null && signature.length > 0) {
signature = AES.AESDecrypt(signature, session.getSecret(), true);
}
if (signature != null && signature.length > 0) {
basedSign = Base64.getEncoder().encodeToString(signature);
}
if (!m_messagesStore.checkSignature(basedSign)) {
LOG.error("Bad signature of session <{}, {}>", session.getUsername(), session.getClientID());
failedBadSignature(channel);
return false;
}
}
session.setMqttVersion(mqttVersion);
} else {
LOG.error("Client didn't supply any password and MQTT anonymous mode is disabled CId={}", clientId);
@@ -446,6 +465,11 @@ public class ProtocolProcessor {
LOG.info("Client {} failed to connect with bad username or password.", session);
}
private void failedBadSignature(Channel session) {
session.writeAndFlush(connAck(CONNECTION_REFUSED_SIGNATURE_FAILURE));
LOG.info("Client {} failed to connect with bad signature.", session);
}
private void setIdleTime(ChannelPipeline pipeline, int idleTime) {
if (pipeline.names().contains("idleStateHandler")) {
pipeline.remove("idleStateHandler");
@@ -32,6 +32,7 @@ public final class MqttConnectPayload {
private final byte[] willMessage;
private final String userName;
private final byte[] password;
private final byte[] signature;
/**
* @deprecated use {@link MqttConnectPayload#MqttConnectPayload(String,
@@ -50,7 +51,7 @@ public final class MqttConnectPayload {
willTopic,
willMessage.getBytes(CharsetUtil.UTF_8),
userName,
password.getBytes(CharsetUtil.UTF_8));
password.getBytes(CharsetUtil.UTF_8), null);
}
public MqttConnectPayload(
@@ -64,7 +65,7 @@ public final class MqttConnectPayload {
willTopic,
willMessage,
userName,
password);
password, null);
}
public MqttConnectPayload(
@@ -73,13 +74,15 @@ public final class MqttConnectPayload {
String willTopic,
byte[] willMessage,
String userName,
byte[] password) {
byte[] password,
byte[] signature) {
this.clientIdentifier = clientIdentifier;
this.willProperties = MqttProperties.withEmptyDefaults(willProperties);
this.willTopic = willTopic;
this.willMessage = willMessage;
this.userName = userName;
this.password = password;
this.signature = signature;
}
public String clientIdentifier() {
@@ -122,6 +125,10 @@ public final class MqttConnectPayload {
return password;
}
public byte[] signatureInBytes() {
return signature;
}
@Override
public String toString() {
return new StringBuilder(StringUtil.simpleClassName(this))
@@ -29,6 +29,7 @@ public enum MqttConnectReturnCode {
CONNECTION_REFUSED_NOT_AUTHORIZED((byte) 0x05),
CONNECTION_REFUSED_UNEXPECT_NODE((byte) 0X06),
CONNECTION_REFUSED_SESSION_NOT_EXIST((byte) 0x07),
CONNECTION_REFUSED_SIGNATURE_FAILURE((byte) 0x08),
//MQTT 5 codes
CONNECTION_REFUSED_UNSPECIFIED_ERROR((byte) 0x80),
CONNECTION_REFUSED_MALFORMED_PACKET((byte) 0x81),
@@ -146,7 +146,7 @@ public final class MqttDecoder extends ReplayingDecoder<MqttDecoder.DecoderState
* bytes for the remaining length.
*
* @see
* https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180841
* <a href="https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180841">...</a>
*
* @param buffer the buffer to decode from
* @return the fixed header
@@ -534,7 +534,7 @@ public final class MqttDecoder extends ReplayingDecoder<MqttDecoder.DecoderState
final MqttProperties willProperties;
if (mqttConnectVariableHeader.isWillFlag()) {
if (mqttVersion == MqttVersion.MQTT_5) {
if (mqttVersion.protocolLevel() <= MqttVersion.MQTT_5.protocolLevel()) {
final Result<MqttProperties> propertiesResult = decodeProperties(buffer);
willProperties = propertiesResult.value;
numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed;
@@ -550,6 +550,7 @@ public final class MqttDecoder extends ReplayingDecoder<MqttDecoder.DecoderState
}
Result<String> decodedUserName = null;
byte[] decodedPassword = null;
byte[] decodedSignature = null;
if (mqttConnectVariableHeader.hasUserName()) {
decodedUserName = decodeString(buffer);
numberOfBytesConsumed += decodedUserName.numberOfBytesConsumed;
@@ -558,6 +559,10 @@ public final class MqttDecoder extends ReplayingDecoder<MqttDecoder.DecoderState
decodedPassword = decodeByteArray(buffer);
numberOfBytesConsumed += decodedPassword.length + 2;
}
if (mqttConnectVariableHeader.isWillRetain()) {
decodedSignature = decodeByteArray(buffer);
numberOfBytesConsumed += decodedSignature.length + 2;
}
final MqttConnectPayload mqttConnectPayload =
new MqttConnectPayload(
@@ -566,7 +571,9 @@ public final class MqttDecoder extends ReplayingDecoder<MqttDecoder.DecoderState
decodedWillTopic != null ? decodedWillTopic.value : null,
decodedWillMessage,
decodedUserName != null ? decodedUserName.value : null,
decodedPassword);
decodedPassword,
decodedSignature
);
return new Result<MqttConnectPayload>(mqttConnectPayload, numberOfBytesConsumed);
}
@@ -208,7 +208,7 @@ public final class MqttMessageBuilders {
keepAliveSecs,
properties);
MqttConnectPayload mqttConnectPayload =
new MqttConnectPayload(clientId, willProperties, willTopic, willMessage, username, password);
new MqttConnectPayload(clientId, willProperties, willTopic, willMessage, username, password, null);
return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
}
}
@@ -88,7 +88,7 @@ public class HttpUtils {
System.out.println(logText);
e.printStackTrace();
LOG.error(logText);
LOG.error("POST to {} with data {} failure", url, jsonStr);
LOG.error("POST to {} failed", url);
if(httpCallback != null) {
httpCallback.onFailure(-1, e.getMessage());
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.98</version>
<version>0.99</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -19,6 +19,8 @@ public class ProtoConstants {
int GroupType_Free = 1;
//member can only quit, owner can do all the operations
int GroupType_Restricted = 2;
//member can not do anything, server api manage the group
int GroupType_Organization = 3;
}
//message GroupMember -> type
+1 -1
View File
@@ -6,7 +6,7 @@
<!--<relativePath>../pom.xml</relativePath>-->
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.98</version>
<version>0.99</version>
</parent>
<artifactId>distribution</artifactId>
+21 -2
View File
@@ -29,6 +29,11 @@
<directory>../systemd/</directory>
<outputDirectory>/systemd</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/checker/</directory>
<outputDirectory>checker/</outputDirectory>
</fileSet>
</fileSets>
<files>
@@ -49,7 +54,7 @@
</file>
<file>
<source>src/main/files/release_note.md</source>
<source>../release_note.md</source>
</file>
<file>
@@ -75,8 +80,22 @@
<outputDirectory>server_sdk/</outputDirectory>
<destName>common-${project.version}.jar</destName>
</file>
</files>
<file>
<source>../sdk/target/checker.jar</source>
<outputDirectory>checker/</outputDirectory>
</file>
<file>
<source>../docker/Dockerfile</source>
<outputDirectory>docker/</outputDirectory>
</file>
<file>
<source>../docker/README.md</source>
<outputDirectory>docker/</outputDirectory>
</file>
</files>
<dependencySets>
<dependencySet>
+5
View File
@@ -0,0 +1,5 @@
部署IM服务后,可以使用Server SDK通过API来检查服务的部署情况,可以执行:
```shell
java -jar checker.jar http://127.0.0.1:18080 123456 http://127.0.0.1 true false
```
来检查。检查之后会产生垃圾数据,请在检查之后停掉IM服务后,再删除掉IM服务数据库。此检查仅检查API,检查通过并不代表部署肯定成功,还需要客户端来验证。
@@ -8,8 +8,13 @@ if [ ! -d /etc/im-server ]; then
mkdir /etc/im-server
fi
if [ ! -d /var/log/im-server ]; then
mkdir /var/log/im-server
fi
mv -f /opt/im-server/config /etc/im-server
systemctl daemon-reload
echo "IM server folders:"
echo "/etc/im-server/config config"
echo "/opt/im-server binary files"
@@ -56,6 +56,12 @@ token.key testim
##token的过期时间,单位为毫秒,默认为无限期。如果需要设置无限期,客户端上一定需要加上token过期的处理。token过期的处理请参考文档的常见问题
##token.expire_time 2592000000
##客户端(目前只有android)的应用签名(一个应用最取第一个签名),如果有多个签名(多个客户端),用英文逗号分开。获取签名方法请参考文档:https://docs.wildfirechat.cn/blogs/签名验证.html
#connect.client_signature_list eNkezQ1g9OsnhfLSFUY1vzKDzhs=,xykezQ1g9OsnhfLSFUY1vzKDzhs=
##是否拒绝空签名的android客户端, 默认为true。当有旧的客户端需要兼容时改成false,当所有都是新客户端时,改成true。
#connect.reject_empty_signature true
##首次登录,是否接收之前的历史消息。
##0 不接收历史消息,只接收${message.compensate_time_limit}毫秒以内的消息,由于服务器没有保存已经收取记录,所以如果有超过${message.compensate_time_limit}毫秒之前未收取的消息也不会收取下拉;
##1接收,会接收 message.max_queue 配置的条数的历史消息
@@ -76,6 +82,9 @@ message.chatroom_remote_history_message 1
##是否禁止陌生人聊天
message.disable_stranger_chat false
##当禁止陌生人聊天时,允许聊天的用户id,比如管理员或者文件传输助手等。用户id以英文逗号分割。
message.allow_stranger_chat_list admin,FireRobot,wfc_file_transfer
##黑名单策略,0 发送失败,返回被拉黑的错误码;1 发送成功但消息被服务器直接丢弃
message.blacklist.strategy 0
@@ -289,15 +298,18 @@ push.ios.server.address http://localhost:8085/ios/push
##消息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.forward.url http://localhost:8087/im_event/message
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3
#message.forward.url http://localhost:8888/im_event/message
##需要转发的消息类型,当有多个时以英文逗号分割,可以用-来指定区间,注意区间不能太大,因为要把区间内的所有数字都放入到Set中。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3,5-10
##需要转发的消息类型,当有多个时以英文逗号分割,可以用-来指定区间,注意区间不能太大,因为要把区间内的所有数字都放入到Set中。
#message.forward.exclude_types 1,2,3,5-10
##敏感消息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.sensitive.forward.url http://localhost:8888/im_event/message
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。
##此转发不受${message.forward.types}和${message.forward.exclude_types}限制。
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
+4
View File
@@ -12,6 +12,10 @@ if [ ! -d /etc/im-server ]; then
mkdir /etc/im-server
fi
if [ ! -d /var/log/im-server ]; then
mkdir /var/log/im-server
fi
mv -f /opt/im-server/config /etc/im-server
systemctl daemon-reload
@@ -18,7 +18,6 @@ while [ -h "$PRG" ]; do
done
PRGDIR=`dirname "$PRG"`
cd $PRGDIR
cd ..
WILDFIRECHAT_HOME=`pwd`
+1 -1
View File
@@ -1,6 +1,6 @@
FROM openjdk:8-jre-alpine
ADD ../distribution/target/distribution-*-bundle-tar.tar.gz /opt/im-server/
ADD distribution-*-bundle-tar.tar.gz /opt/im-server/
RUN sed -i 's/#JAVA_OPTS="$JAVA_OPTS -Xmx128M"/JAVA_OPTS="$JAVA_OPTS -Xmx$JVM_XMX"/g' /opt/im-server/bin/wildfirechat.sh
RUN sed -i 's/#JAVA_OPTS="$JAVA_OPTS -Xms128M"/JAVA_OPTS="$JAVA_OPTS -Xms$JVM_XMS"/g' /opt/im-server/bin/wildfirechat.sh
+3 -2
View File
@@ -5,17 +5,18 @@
```
mvn clean package
```
生成的软件包在```distribution/target/distribution-XX-bundle-tar.tar.gz```,把此软件包拷贝到docker目录下。
然后进入到docker目录下,编译镜像
```
sudo docker build -t im-server -f Dockerfile ..
sudo docker build -t im-server .
```
## 运行
配置:
运行需要手动指定配置目录,手动指定配置目录的方法如下,其中$PATH_TO_CONFIG为im配置目录,需要为绝对路径,模版为```distribution/src/main/resources```目录。另外需要创建h2数据库目录(如果用mysql就不需要了)、日志目录、内置存储服务目录(如果使用其它对象存储服务就不需要了)。
```
sudo docker run -it -v $PATH_TO_CONFIG:/opt/im-server/config -v $PATH_TO_LOGS:/opt/im-server/logs -v $PATH_TO_H2DB:/opt/im-server/h2db -v $PATH_TO_MEDIA:/opt/im-server/media -e JVM_XMX=256M -e JVM_XMS=256M -p 80:80 -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 18080:18080 im-server
sudo docker run -it --name im-server -v $PATH_TO_CONFIG:/opt/im-server/config -v $PATH_TO_LOGS:/opt/im-server/logs -v $PATH_TO_H2DB:/opt/im-server/h2db -v $PATH_TO_MEDIA:/opt/im-server/media -e JVM_XMX=256M -e JVM_XMS=256M -p 80:80 -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 18080:18080 im-server
```
## 导出镜像
+1 -1
View File
@@ -12,7 +12,7 @@
<artifactId>wildfirechat-parent</artifactId>
<packaging>pom</packaging>
<version>0.98</version>
<version>0.99</version>
<name>Wildfire Chat</name>
<description>Wildfire Chat Server</description>
<inceptionYear>2019</inceptionYear>
@@ -12,6 +12,16 @@
# 更新记录
--------------
Release note 0.99:
1. API获取用户信息时,过滤掉已经删除用户。
2. 消息转发功能支持设置include/exclude功能,支持按区间过滤。
3. 添加新的群组类型:组织群。组织群适用于公司部门群场景。
4. 内置对象存储支持绝对路径和相对路径配置
5. 当开启禁止陌生人聊天时,可以添加例外人员
6. 解决user setting并发问题
7. 添加可选的android平台签名验证功能。
--------------
Release note 0.98:
1. 解决linux多端登录错误问题
+33 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.98</version>
<version>0.99</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -81,4 +81,36 @@
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}-${version}</finalName>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>checker</finalName>
<appendAssemblyId>false</appendAssemblyId>
<archive>
<manifest>
<mainClass>cn.wildfirechat.sdk.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -31,6 +31,20 @@ public class Main {
public static void main(String[] args) throws Exception {
if (args.length == 5) {
AdminUrl = args[0];
AdminSecret = args[1];
IMUrl = args[2];
commercialServer = Boolean.parseBoolean(args[3]);
advanceVoip = Boolean.parseBoolean(args[4]);
} else {
System.out.println("Usage: java -jar checker.jar adminUrl adminSecret imUrl commercialServer advanceVoip \n e.g. java -jar checker.jar http://192.168.1.80:18080 123456 http://192.168.1.80 false false");
System.out.println();
System.out.println();
System.out.println("Use default value: java -jar checker.jar http://127.0.0.1:18080 123456 http://127.0.0.1 false false");
}
//admin使用的是18080端口,超级管理接口,理论上不能对外开放端口,也不能让非内部服务知悉密钥。
testAdmin();