APP特征配置导入页修改,app payload增加配置导入功能.
This commit is contained in:
@@ -1036,6 +1036,11 @@ public class ExportExcel {
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("default_value")+":"+defaultValue+"\n";
|
||||
index++;
|
||||
}
|
||||
// APP Payload
|
||||
if(region.getFunctionId().equals(563)) {
|
||||
commentStr=commentStr+"▶"+"L3_header:"+msgProp.getProperty("need_input")+"'headerType'"+"\n";
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1257,6 +1262,95 @@ public class ExportExcel {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// APP Payload
|
||||
if("headerType".equals(headerStr)){
|
||||
commentStr=commentStr+"IP_header"+"\n"+"ICMP_header"+"\n";
|
||||
index++;
|
||||
index++;
|
||||
defaultValue=region.getConfigDistrict().split(",")[0];
|
||||
|
||||
commentStr=msgProp.getProperty("select")+":\n"+commentStr;
|
||||
index++;
|
||||
index++;
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
|
||||
//1、非空说明
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("required")+"\n";
|
||||
index++;
|
||||
//2、默认值说明
|
||||
/*if(!StringUtil.isEmpty(defaultValue)){
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("default_value")+":"+"IP_header"+"\n";
|
||||
index++;
|
||||
}*/
|
||||
|
||||
commentStr=commentStr+"▶"+"IP_header:"+msgProp.getProperty("need_input")+"('VER'、'IHL'、'TOS'、'Total Length'、'Flags'、'fragment offset'、'Protocol'),"+msgProp.getProperty("max_input")+"\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+"ICMP_header:"+msgProp.getProperty("need_input")+"('ICMP type'、'ICMP code'、'ICMP identifier')"+"\n";
|
||||
index++;
|
||||
}
|
||||
if("VER".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_4");
|
||||
index++;
|
||||
}
|
||||
if("IHL".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_4");
|
||||
index++;
|
||||
}
|
||||
if("TOS".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_8");
|
||||
index++;
|
||||
}
|
||||
if("Total Length".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_16");
|
||||
index++;
|
||||
}
|
||||
if("Flags".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_3");
|
||||
index++;
|
||||
}
|
||||
if("fragment offset".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_8");
|
||||
index++;
|
||||
}
|
||||
if("Protocol".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_8");
|
||||
index++;
|
||||
}
|
||||
if("ICMP type".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_8");
|
||||
index++;
|
||||
}
|
||||
if("ICMP code".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_8");
|
||||
index++;
|
||||
}
|
||||
if("ICMP identifier".equals(headerStr)){
|
||||
commentStr=commentStr+msgProp.getProperty("rule_desc_tip")+":\n";
|
||||
index++;
|
||||
commentStr=commentStr+"▶"+msgProp.getProperty("hex_minlength_16");
|
||||
index++;
|
||||
}
|
||||
|
||||
}else if(region.getRegionType().equals(6)) {
|
||||
if(region.getFunctionId().equals(400)) {
|
||||
if("group".equals(headerStr)){
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
package com.nis.util.excel.thread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jets3t.service.ServiceException;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import com.beust.jcommander.internal.Sets;
|
||||
import com.nis.domain.FunctionRegionDict;
|
||||
import com.nis.domain.FunctionServiceDict;
|
||||
import com.nis.domain.configuration.AppComplexFeatureCfg;
|
||||
import com.nis.util.Constants;
|
||||
import com.nis.web.service.SpringContextHolder;
|
||||
import com.nis.web.service.configuration.AppMultiFeatureCfgService;
|
||||
|
||||
public class CheckAppFeatureComplexStringFormatThread implements Callable<String>{
|
||||
private Logger logger=Logger.getLogger(CheckAppFeatureComplexStringFormatThread.class);
|
||||
private BlockingQueue<? extends Object> srcQueue;
|
||||
private BlockingQueue<AppComplexFeatureCfg> destQueue;
|
||||
private Properties prop;
|
||||
private FunctionServiceDict serviceDict;
|
||||
private FunctionRegionDict regionDict;
|
||||
|
||||
public CheckAppFeatureComplexStringFormatThread(FunctionServiceDict serviceDict,FunctionRegionDict regionDict,Properties prop,BlockingQueue<? extends Object> srcQueue,BlockingQueue<AppComplexFeatureCfg> destQueue) {
|
||||
this.serviceDict=serviceDict;
|
||||
this.regionDict=regionDict;
|
||||
this.srcQueue=srcQueue;
|
||||
this.destQueue=destQueue;
|
||||
this.prop=prop;
|
||||
|
||||
}
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
List<Object> dataList=Lists.newArrayList(Constants.MAAT_JSON_SEND_SIZE);
|
||||
StringBuffer msg=new StringBuffer();
|
||||
while(!srcQueue.isEmpty()) {
|
||||
int size=srcQueue.drainTo(dataList,Constants.MAAT_JSON_SEND_SIZE);
|
||||
if(regionDict.getRegionType().intValue()==3) {
|
||||
try {
|
||||
List<AppComplexFeatureCfg> cfgs=this.checkAppComplexFeatureCfg(msg,dataList);
|
||||
destQueue.addAll(cfgs);
|
||||
}catch (Exception e) {
|
||||
logger.error("checkComplexStringCfg error,",e);
|
||||
// TODO: handle exception
|
||||
//msg=e.getMessage();
|
||||
//msg.append(e.getMessage());
|
||||
//break;
|
||||
}
|
||||
}
|
||||
dataList.clear();
|
||||
}
|
||||
return msg.toString();
|
||||
}
|
||||
public List<AppComplexFeatureCfg> checkAppComplexFeatureCfg(StringBuffer msg,
|
||||
List<?> list) throws ServiceException {
|
||||
logger.warn("start to validate appfeaturecomplexString data...");
|
||||
long start=System.currentTimeMillis();
|
||||
List<AppComplexFeatureCfg> stringList = new ArrayList<AppComplexFeatureCfg>();
|
||||
String exprTypeP = regionDict.getConfigExprType();
|
||||
if (StringUtils.isBlank(exprTypeP)) {
|
||||
throw new RuntimeException("Found String region,but exprType is Empty");
|
||||
}
|
||||
String matchMethodP = regionDict.getConfigMatchMethod();
|
||||
if (StringUtils.isBlank(matchMethodP)) {
|
||||
throw new RuntimeException("Found String region,but matchMethod is Empty");
|
||||
}
|
||||
String hexP = regionDict.getConfigHex();
|
||||
if (StringUtils.isBlank(hexP)) {
|
||||
throw new RuntimeException("Found String region,but hex is Empty");
|
||||
}
|
||||
String mulityKeywordsP = regionDict.getConfigMultiKeywords();
|
||||
if (StringUtils.isBlank(mulityKeywordsP)) {
|
||||
throw new RuntimeException("Found String region,but mulityKeywords is Empty");
|
||||
}
|
||||
String dirtrictP = regionDict.getConfigDistrict();
|
||||
StringBuffer errTip = new StringBuffer();
|
||||
Pattern pattern = Pattern.compile("\t|\r|\n|\b|\f");
|
||||
AppMultiFeatureCfgService appMultiFeatureCfgService = SpringContextHolder.getBean(AppMultiFeatureCfgService.class);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
StringBuffer errInfo = new StringBuffer();
|
||||
AppComplexFeatureCfg baseStringCfg = new AppComplexFeatureCfg();
|
||||
BeanUtils.copyProperties(list.get(i), baseStringCfg);
|
||||
// 配置描述、关键字长度限制
|
||||
if(baseStringCfg.getCfgDesc().length() > 128) {
|
||||
errInfo.append(prop.getProperty("config_describe")+prop.getProperty("length_error")+" "+String.format(prop.getProperty("max_length")+":128") + ";");
|
||||
}
|
||||
if(baseStringCfg.getCfgKeywords().length() < 4 || baseStringCfg.getCfgKeywords().length() > 1024){
|
||||
errInfo.append(prop.getProperty("key_word")+prop.getProperty("length_error")+" "+String.format(prop.getProperty("min_length")+":4,"+prop.getProperty("max_length")+":1024") + ";");
|
||||
}
|
||||
|
||||
if (regionDict.getRegionType().equals(3)) {
|
||||
String keyword = baseStringCfg.getCfgKeywords();
|
||||
String district = baseStringCfg.getDistrict();
|
||||
if (StringUtils.isBlank(keyword)) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("can_not_null"), prop.getProperty("key_word") + " ") + ";");
|
||||
}
|
||||
if (StringUtils.isNotBlank(dirtrictP)) {
|
||||
if (StringUtils.isBlank(district)) {
|
||||
if (dirtrictP.indexOf(",") == -1) {
|
||||
baseStringCfg.setDistrict(dirtrictP);
|
||||
} else {
|
||||
// baseStringCfg.setDistrict(dirtrictP.split(",")[0]);
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("can_not_null"), prop.getProperty("district") + " ")
|
||||
+ ";");
|
||||
}
|
||||
} else if (dirtrictP.indexOf(district) == -1) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("district") + " ")
|
||||
+ ";");
|
||||
} else if (dirtrictP.indexOf("others")>-1&&district.equals("others")) {
|
||||
//不允许自定义匹配区域导入
|
||||
errInfo.append(prop.getProperty("district")+" "+
|
||||
String.format(prop.getProperty("can_not_be"), " 'others'")+ ";");
|
||||
}
|
||||
}
|
||||
if (mulityKeywordsP.equals("0")) {
|
||||
if (keyword.indexOf("\n") > -1) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("not_multiple"), prop.getProperty("key_word")) + ";");
|
||||
}
|
||||
Matcher m = pattern.matcher(keyword);
|
||||
if (m.find()) {
|
||||
errInfo.append(String.format(prop.getProperty("has_invisible_char"),
|
||||
prop.getProperty("key_word") + " '" + keyword + "'") + ";");
|
||||
}
|
||||
} else {
|
||||
boolean has = false;
|
||||
Set<String> keywordSet=Sets.newHashSet();
|
||||
|
||||
for (String key : keyword.split("\n")) {
|
||||
Matcher m = pattern.matcher(key);
|
||||
if (m.find()) {
|
||||
has = true;
|
||||
errInfo.append(String.format(prop.getProperty("has_invisible_char"),
|
||||
prop.getProperty("key_word") + " '" + key + "'") + ";");
|
||||
break;
|
||||
}
|
||||
if(!keywordSet.contains(key)) {
|
||||
keywordSet.add(key);
|
||||
}else {
|
||||
errInfo.append(prop.getProperty("key_word") + " '" + key + "'"+" "+prop.getProperty("repeat") + ";");
|
||||
}
|
||||
}
|
||||
if (!has) {
|
||||
if(keyword.replaceAll("\n","").length()>1024) {
|
||||
errInfo.append(String.format(prop.getProperty("most_keywords"),
|
||||
prop.getProperty("key_word")) + ";");
|
||||
}else {
|
||||
String reWord = keyword.replaceAll("\n", Constants.KEYWORD_EXPR);
|
||||
baseStringCfg.setCfgKeywords(reWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
Integer exprType = baseStringCfg.getExprType();
|
||||
boolean has = false;
|
||||
if (exprType == null) {
|
||||
if (exprTypeP.indexOf(",") == -1) {
|
||||
if (mulityKeywordsP.equals("0") && exprTypeP.equals("1")) {
|
||||
throw new RuntimeException("region dict config error,dict id is " + regionDict.getDictId());
|
||||
}
|
||||
baseStringCfg.setExprType(Integer.parseInt(exprTypeP));
|
||||
} else if (exprTypeP.indexOf("0") > -1 && mulityKeywordsP.equals("0")) {
|
||||
baseStringCfg.setExprType(0);
|
||||
} else if (exprTypeP.indexOf("1") > -1 && mulityKeywordsP.equals("1")
|
||||
&& keyword.indexOf("\n") > -1) {
|
||||
baseStringCfg.setExprType(1);
|
||||
} else if (exprTypeP.indexOf("0") > -1 && mulityKeywordsP.equals("1")
|
||||
&& keyword.indexOf("\n") == -1) {
|
||||
baseStringCfg.setExprType(0);
|
||||
} else {
|
||||
baseStringCfg.setExprType(Integer.parseInt(exprTypeP.split(",")[0]));
|
||||
}
|
||||
// errInfo.append(String.format(prop.getProperty("can_not_null"),
|
||||
// prop.getProperty("expression_type"))+";");
|
||||
} else {
|
||||
for (String exp : exprTypeP.split(",")) {
|
||||
if (exp.equals(exprType.toString())) {
|
||||
has = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("expression_type"))
|
||||
+ ";");
|
||||
}
|
||||
has = false;
|
||||
}
|
||||
exprType = baseStringCfg.getExprType();
|
||||
Integer matchMethod = baseStringCfg.getMatchMethod();
|
||||
if (matchMethod == null) {
|
||||
if (matchMethodP.indexOf(",") == -1) {
|
||||
if (exprTypeP.equals("1") && !matchMethodP.equals("0")) {
|
||||
throw new RuntimeException("region dict config error,dict id is " + regionDict.getDictId());
|
||||
}
|
||||
baseStringCfg.setMatchMethod(Integer.parseInt(matchMethodP));
|
||||
} else if (exprType != null && exprType.intValue() == 1) {
|
||||
if (matchMethodP.indexOf("0") > -1) {
|
||||
baseStringCfg.setMatchMethod(0);
|
||||
} else {
|
||||
throw new RuntimeException("region dict config error,dict id is " + regionDict.getDictId());
|
||||
}
|
||||
|
||||
} else {
|
||||
baseStringCfg.setMatchMethod(Integer.parseInt(matchMethodP.split(",")[0]));
|
||||
}
|
||||
// errInfo.append(String.format(prop.getProperty("can_not_null"),
|
||||
// prop.getProperty("match_method"))+";");
|
||||
} else {
|
||||
for (String exp : matchMethodP.split(",")) {
|
||||
if (exp.equals(matchMethod.toString())) {
|
||||
has = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has) {
|
||||
errInfo.append(String.format(prop.getProperty("is_incorrect"), prop.getProperty("match_method"))
|
||||
+ ";");
|
||||
}
|
||||
}
|
||||
Integer isHex = baseStringCfg.getIsHex();
|
||||
Integer isCaseInsenstive = baseStringCfg.getIsCaseInsenstive();
|
||||
if (isHex == null || isCaseInsenstive == null) {
|
||||
if (isHex == null) {
|
||||
if (hexP.indexOf("0")>-1 || hexP.indexOf("2")>-1) {
|
||||
baseStringCfg.setIsHex(0);
|
||||
} else if (hexP.indexOf("1")>-1) {
|
||||
baseStringCfg.setIsHex(1);
|
||||
} else {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("can_not_null"), prop.getProperty("is_hex")) + ";");
|
||||
}
|
||||
}
|
||||
if (isCaseInsenstive == null) {
|
||||
if (hexP.indexOf("0")>-1 || hexP.indexOf("1")>-1) {
|
||||
baseStringCfg.setIsCaseInsenstive(0);
|
||||
} else if (hexP.indexOf("2")>-1) {
|
||||
baseStringCfg.setIsCaseInsenstive(1);
|
||||
} else {
|
||||
errInfo.append(String.format(prop.getProperty("can_not_null"),
|
||||
prop.getProperty("is_case_insenstive")) + ";");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isHex.intValue() != 0 && isHex.intValue() != 1) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("is_hex")) + ";");
|
||||
}
|
||||
if (isCaseInsenstive.intValue() != 0 && isCaseInsenstive.intValue() != 1) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("is_case_insenstive"))
|
||||
+ ";");
|
||||
}
|
||||
if (hexP.indexOf("1") == -1 && isHex.intValue() == 1) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("is_hex")) + ";");
|
||||
}
|
||||
if (hexP.equals("1") && isHex.intValue() == 0) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("is_hex")) + ";");
|
||||
}
|
||||
if (hexP.indexOf("2") == -1 && isCaseInsenstive.intValue() == 1) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("is_case_insenstive"))
|
||||
+ ";");
|
||||
}
|
||||
if (hexP.equals("2") && isCaseInsenstive.intValue() == 0) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), prop.getProperty("is_case_insenstive"))
|
||||
+ ";");
|
||||
}
|
||||
// 关键字十六进制校验
|
||||
if (hexP.indexOf("1") != -1 && isHex.intValue() == 1) {
|
||||
boolean bl = Pattern.compile("^([0-9|a-f|A-F]*)$").matcher(keyword).matches();
|
||||
if(!bl) {
|
||||
errInfo.append(
|
||||
prop.getProperty("key_word") + " '" + keyword + "' "+String.format(prop.getProperty("contains_non_hex_char")) + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
isHex = baseStringCfg.getIsHex();
|
||||
isCaseInsenstive = baseStringCfg.getIsCaseInsenstive();
|
||||
if (isHex != null && isCaseInsenstive != null) {
|
||||
if (isHex.intValue() == 0 && isCaseInsenstive.intValue() == 0) {
|
||||
baseStringCfg.setIsHexbin(0);
|
||||
} else if (isHex.intValue() == 1 && isCaseInsenstive.intValue() == 0) {
|
||||
baseStringCfg.setIsHexbin(1);
|
||||
} else if (isHex.intValue() == 0 && isCaseInsenstive.intValue() == 1) {
|
||||
baseStringCfg.setIsHexbin(2);
|
||||
} else if (hexP.indexOf("1") != -1 && isHex.intValue() == 1 && isCaseInsenstive.intValue() == 1) {// 只有是十六进制且取值正确的时候, 才进行(十六进制)与(大小写敏感)的校验
|
||||
errInfo.append(prop.getProperty("hex_case_insensitive")+ ";");
|
||||
}
|
||||
}
|
||||
|
||||
// APP Payload L3_HEADER的特殊属性限制
|
||||
if(baseStringCfg.getDistrict().equals("L3_header")) {
|
||||
String headerType = baseStringCfg.getHeaderType();
|
||||
if(StringUtils.isBlank(headerType)) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("can_not_null"), "headerType") + ";");
|
||||
}else if((!"IP_header".equals(headerType)) && (!"ICMP_header".equals(headerType))) {
|
||||
errInfo.append(
|
||||
String.format(prop.getProperty("is_incorrect"), "headerType") + ";");
|
||||
}else{
|
||||
appMultiFeatureCfgService.checkImportAppPayload(baseStringCfg, errInfo, prop);
|
||||
}
|
||||
appMultiFeatureCfgService.setL3HeaderKeyword(baseStringCfg);
|
||||
}else {
|
||||
baseStringCfg.setHeaderType(null);
|
||||
baseStringCfg.setVer(null);
|
||||
baseStringCfg.setIhl(null);
|
||||
baseStringCfg.setTos(null);
|
||||
baseStringCfg.setTotalLength(null);
|
||||
baseStringCfg.setFlags(null);
|
||||
baseStringCfg.setFragmentOffset(null);
|
||||
baseStringCfg.setProtocol(null);
|
||||
baseStringCfg.setIcmpType(null);
|
||||
baseStringCfg.setIcmpCode(null);
|
||||
baseStringCfg.setIcmpIdentifier(null);
|
||||
}
|
||||
}
|
||||
if (errInfo.toString().length() > 0) {
|
||||
errTip.append(String.format(prop.getProperty("line"), baseStringCfg.getIndex()) + ",");
|
||||
errTip.append(errInfo);
|
||||
errTip.append("<br>");
|
||||
}else {
|
||||
stringList.add(baseStringCfg);
|
||||
}
|
||||
}
|
||||
if (errTip.toString().length() > 0) {
|
||||
msg.append(errTip);
|
||||
//throw new ServiceException(errTip.toString());
|
||||
}
|
||||
long end=System.currentTimeMillis();
|
||||
logger.warn("validate appfeaturecomplexString data finish,cost:"+(end-start));
|
||||
return stringList;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user