package com.ard.gb28181.transmit.event.request;
|
|
import com.google.common.primitives.Bytes;
|
import com.ard.gb28181.transmit.SIPSender;
|
import com.ard.gb28181.utils.IpPortUtil;
|
import com.ard.gb28181.api.utils.SipUtils;
|
import gov.nist.javax.sip.message.SIPRequest;
|
import gov.nist.javax.sip.message.SIPResponse;
|
import lombok.extern.slf4j.Slf4j;
|
import org.dom4j.Document;
|
import org.dom4j.DocumentException;
|
import org.dom4j.Element;
|
import org.dom4j.io.SAXReader;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.util.ObjectUtils;
|
|
import javax.sip.*;
|
import javax.sip.address.Address;
|
import javax.sip.address.SipURI;
|
import javax.sip.header.ContentTypeHeader;
|
import javax.sip.header.ExpiresHeader;
|
import javax.sip.header.HeaderFactory;
|
import javax.sip.message.MessageFactory;
|
import javax.sip.message.Request;
|
import java.io.ByteArrayInputStream;
|
import java.text.ParseException;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.List;
|
|
/**
|
* @description:处理接收IPCamera发来的SIP协议请求消息
|
* @author: songww
|
* @date: 2020年5月3日 下午4:42:22
|
*/
|
@Slf4j
|
public abstract class SIPRequestProcessorParent {
|
|
@Autowired
|
private SIPSender sipSender;
|
|
public HeaderFactory getHeaderFactory() {
|
try {
|
return SipFactory.getInstance().createHeaderFactory();
|
} catch (PeerUnavailableException e) {
|
log.error("未处理的异常 ", e);
|
}
|
return null;
|
}
|
|
public MessageFactory getMessageFactory() {
|
try {
|
return SipFactory.getInstance().createMessageFactory();
|
} catch (PeerUnavailableException e) {
|
log.error("未处理的异常 ", e);
|
}
|
return null;
|
}
|
|
class ResponseAckExtraParam {
|
String content;
|
ContentTypeHeader contentTypeHeader;
|
SipURI sipURI;
|
int expires = -1;
|
}
|
|
/***
|
* 回复状态码
|
* 100 trying
|
* 200 OK
|
* 400
|
* 404
|
*/
|
public SIPResponse responseAck(SIPRequest sipRequest, int statusCode) throws SipException, InvalidArgumentException, ParseException {
|
return responseAck(sipRequest, statusCode, null);
|
}
|
|
public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
|
return responseAck(sipRequest, statusCode, msg, null);
|
}
|
|
|
public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException {
|
if (sipRequest.getToHeader().getTag() == null) {
|
sipRequest.getToHeader().setTag(SipUtils.getNewTag());
|
}
|
SIPResponse response = (SIPResponse) getMessageFactory().createResponse(statusCode, sipRequest);
|
response.setStatusCode(statusCode);
|
if (msg != null) {
|
response.setReasonPhrase(msg);
|
}
|
|
if (responseAckExtraParam != null) {
|
if (responseAckExtraParam.sipURI != null && sipRequest.getMethod().equals(Request.INVITE)) {
|
log.debug("responseSdpAck SipURI: {}:{}", responseAckExtraParam.sipURI.getHost(), responseAckExtraParam.sipURI.getPort());
|
Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(
|
SipFactory.getInstance().createAddressFactory().createSipURI(responseAckExtraParam.sipURI.getUser(), IpPortUtil.concatenateIpAndPort(responseAckExtraParam.sipURI.getHost(), String.valueOf(responseAckExtraParam.sipURI.getPort()))
|
));
|
response.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
|
}
|
if (responseAckExtraParam.contentTypeHeader != null) {
|
response.setContent(responseAckExtraParam.content, responseAckExtraParam.contentTypeHeader);
|
}
|
|
if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) {
|
if (responseAckExtraParam.expires == -1) {
|
log.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header");
|
} else {
|
ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(responseAckExtraParam.expires);
|
response.addHeader(expiresHeader);
|
}
|
}
|
} else {
|
if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) {
|
log.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header");
|
}
|
}
|
|
// 发送response
|
sipSender.transmitRequest(sipRequest.getLocalAddress().getHostAddress(), response);
|
|
return response;
|
}
|
|
public Element getRootElement(RequestEvent evt) throws DocumentException {
|
return getRootElement(evt, "gb2312");
|
}
|
|
public Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
|
|
byte[] rawContent = evt.getRequest().getRawContent();
|
if (evt.getRequest().getContentLength().getContentLength() == 0
|
|| rawContent == null
|
|| rawContent.length == 0
|
|| ObjectUtils.isEmpty(new String(rawContent))) {
|
return null;
|
}
|
|
if (charset == null) {
|
charset = "gb2312";
|
}
|
SAXReader reader = new SAXReader();
|
reader.setEncoding(charset);
|
// 对海康出现的未转义字符做处理。
|
String[] destStrArray = new String[]{"<", ">", "&", "'", """};
|
// 或许可扩展兼容其他字符
|
char despChar = '&';
|
byte destBye = (byte) despChar;
|
List<Byte> result = new ArrayList<>();
|
for (int i = 0; i < rawContent.length; i++) {
|
if (rawContent[i] == destBye) {
|
boolean resul = false;
|
for (String destStr : destStrArray) {
|
if (i + destStr.length() <= rawContent.length) {
|
byte[] bytes = Arrays.copyOfRange(rawContent, i, i + destStr.length());
|
resul = resul || (Arrays.equals(bytes, destStr.getBytes()));
|
}
|
}
|
if (resul) {
|
result.add(rawContent[i]);
|
}
|
} else {
|
result.add(rawContent[i]);
|
}
|
}
|
byte[] bytesResult = Bytes.toArray(result);
|
|
Document xml;
|
try {
|
xml = reader.read(new ByteArrayInputStream(bytesResult));
|
} catch (DocumentException e) {
|
log.warn("[xml解析异常]: 原文如下: \r\n{}", new String(bytesResult));
|
log.warn("[xml解析异常]: 原文如下: 尝试兼容性处理");
|
String[] xmlLineArray = new String(bytesResult).split("\\r?\\n");
|
|
// 兼容海康的address字段带有<破换xml结构导致无法解析xml的问题
|
StringBuilder stringBuilder = new StringBuilder();
|
for (String s : xmlLineArray) {
|
if (s.startsWith("<Address")) {
|
continue;
|
}
|
stringBuilder.append(s);
|
}
|
xml = reader.read(new ByteArrayInputStream(stringBuilder.toString().getBytes()));
|
}
|
return xml.getRootElement();
|
}
|
|
|
}
|