1 /*
2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "rtsp_common.h"
17 #include <regex>
18 #include "common/media_log.h"
19
20 namespace OHOS {
21 namespace Sharing {
22
23 constexpr int RTSP_MESSAGE_MAX_LINES = 1440;
24
GetRtspDate()25 std::string RtspCommon::GetRtspDate()
26 {
27 time_t now = time(nullptr);
28 if (now <= 0) {
29 return {};
30 }
31 struct tm t = {0};
32 if (!gmtime_r(&now, &t)) {
33 return {};
34 }
35 char buf[40] = {0};
36 if (strftime(buf, sizeof(buf), "%a, %b %d %Y %H:%M:%S GMT", &t) < 0) {
37 return {};
38 }
39 return buf;
40 }
41
Split(const std::string & str,const std::string & delimiter)42 std::vector<std::string> RtspCommon::Split(const std::string &str, const std::string &delimiter)
43 {
44 std::regex reg{delimiter};
45 return {std::sregex_token_iterator(str.begin(), str.end(), reg, -1), std::sregex_token_iterator()};
46 }
47
SplitParameter(std::list<std::string> & lines,std::list<std::pair<std::string,std::string>> & params)48 void RtspCommon::SplitParameter(std::list<std::string> &lines, std::list<std::pair<std::string, std::string>> ¶ms)
49 {
50 for (auto &line : lines) {
51 if (line.size() > 3) { // 3:fixed size
52 auto index = line.find_first_of(':');
53 if (index != 0 && index + 1 != line.length()) {
54 auto token = RtspCommon::Trim(line.substr(0, index));
55 auto value = RtspCommon::Trim(line.substr(index + 1));
56 params.emplace_back(token, value);
57 }
58 }
59 }
60 }
61
VerifyMethod(const std::string & method)62 bool RtspCommon::VerifyMethod(const std::string &method)
63 {
64 return method == RTSP_METHOD_OPTIONS || method == RTSP_METHOD_DESCRIBE || method == RTSP_METHOD_ANNOUNCE ||
65 method == RTSP_METHOD_SETUP || method == RTSP_METHOD_PLAY || method == RTSP_METHOD_PAUSE ||
66 method == RTSP_METHOD_TEARDOWN || method == RTSP_METHOD_GET_PARAMETER ||
67 method == RTSP_METHOD_SET_PARAMETER || method == RTSP_METHOD_REDIRECT || method == RTSP_METHOD_RECORD;
68 }
69
Trim(const std::string & str)70 std::string RtspCommon::Trim(const std::string &str)
71 {
72 if (str.empty())
73 return str;
74 auto s = str.find_first_not_of(' ');
75 auto e = str.find_last_not_of(' ');
76 if (s == std::string::npos || e == std::string::npos) {
77 return str;
78 }
79
80 return str.substr(s, e + 1 - s);
81 }
82
ParseMessage(const std::string & message,std::vector<std::string> & firstLine,std::unordered_map<std::string,std::string> & header,std::list<std::string> & body)83 RtspError RtspCommon::ParseMessage(const std::string &message, std::vector<std::string> &firstLine,
84 std::unordered_map<std::string, std::string> &header, std::list<std::string> &body)
85 {
86 auto messageV = RtspCommon::Split(message, std::string(RTSP_CRLF) + RTSP_CRLF);
87 auto headers = messageV[0];
88
89 std::vector<std::string> lines = RtspCommon::Split(headers, RTSP_CRLF);
90 if (lines.size() < 2) { // 2:fixed size
91 return {RtspErrorType::INVALID_MESSAGE, "invalid message"};
92 }
93
94 firstLine = RtspCommon::Split(lines[0], RTSP_SP);
95
96 // parse header
97 for (int32_t i = 1; i < (int32_t)lines.size(); ++i) {
98 if (lines[i].size() > 3) { // 3:fixed size
99 auto index = lines[i].find_first_of(':');
100 if (index != 0 && index != std::string::npos && index + 1 != lines[i].length()) {
101 auto token = RtspCommon::Trim(lines[i].substr(0, index));
102 auto value = RtspCommon::Trim(lines[i].substr(index + 1));
103 if (token == "WWW-Authenticate" && value.find("Digest") == std::string::npos) {
104 continue;
105 }
106 header.emplace(token, value);
107 }
108 }
109 if (i > RTSP_MESSAGE_MAX_LINES) {
110 break;
111 }
112 }
113
114 // parse body
115 if (messageV.size() == 2 && header.find(RTSP_TOKEN_CONTENT_TYPE) != header.end() && // 2:fixed size
116 header.find(RTSP_TOKEN_CONTENT_LENGTH) != header.end()) {
117 int32_t length = atoi(header.at(RTSP_TOKEN_CONTENT_LENGTH).c_str());
118 if (length == 0) {
119 SHARING_LOGW("Content-Length == 0.");
120 return {};
121 }
122
123 if (length > (int32_t)messageV[1].length()) {
124 SHARING_LOGE("invalid body: Content-Length: %{public}d, body length: %{public}zu\n!", length,
125 messageV[1].length());
126 return {RtspErrorType::INCOMPLETE_MESSAGE, "body length < Content-Length"};
127 }
128
129 if (header.at(RTSP_TOKEN_CONTENT_TYPE).compare(0, 5, "text/") != 0 && // 5:fixed size
130 header.at(RTSP_TOKEN_CONTENT_TYPE) != "application/sdp") {
131 return {RtspErrorType::INVALID_MESSAGE, "unsupported content"};
132 }
133
134 auto bodyStr = messageV[1].substr(0, length);
135 auto bodyVec = RtspCommon::Split(bodyStr, RTSP_CRLF);
136 for (auto &item : bodyVec) {
137 if (!item.empty()) {
138 body.emplace_back(item);
139 }
140 }
141 } else {
142 if (messageV.size() > 1) {
143 SHARING_LOGW("may packet splicing.");
144 std::string splicingPart;
145 for (size_t i = 1; i < messageV.size(); ++i) {
146 splicingPart += messageV[i];
147 if (i != messageV.size() - 1) {
148 splicingPart += "\r\n\r\n";
149 }
150 }
151 splicingPart += '$';
152 SHARING_LOGW("splicing \n%{public}s!", splicingPart.c_str());
153 return {RtspErrorType::OK, splicingPart};
154 }
155 }
156
157 return {};
158 }
159 } // namespace Sharing
160 } // namespace OHOS