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 #include "utils/crypto.h"
20 
21 namespace OHOS {
22 namespace Sharing {
GetRtspDate()23 std::string RtspCommon::GetRtspDate()
24 {
25     time_t now = time(nullptr);
26     if (now <= 0) {
27         return {};
28     }
29     struct tm *t = gmtime(&now);
30     if (t == nullptr) {
31         return {};
32     }
33     char buf[32] = {0};
34     if (strftime(buf, 128, "%a, %b %d %Y %H:%M:%S GMT", t) < 0) { // 128:fixed size
35         return {};
36     }
37     return buf;
38 }
39 
Split(const std::string &str, const std::string &delimiter)40 std::vector<std::string> RtspCommon::Split(const std::string &str, const std::string &delimiter)
41 {
42     std::regex reg{delimiter};
43     return {std::sregex_token_iterator(str.begin(), str.end(), reg, -1), std::sregex_token_iterator()};
44 }
45 
SplitParameter(std::list<std::string> &lines, std::list<std::pair<std::string, std::string>> &params)46 void RtspCommon::SplitParameter(std::list<std::string> &lines, std::list<std::pair<std::string, std::string>> &params)
47 {
48     for (auto &line : lines) {
49         if (line.size() > 3) { // 3:fixed size
50             auto index = line.find_first_of(':');
51             if (index != 0 && index + 1 != line.length()) {
52                 auto token = RtspCommon::Trim(line.substr(0, index));
53                 auto value = RtspCommon::Trim(line.substr(index + 1));
54                 params.emplace_back(token, value);
55             }
56         }
57     }
58 }
59 
VerifyMethod(const std::string &method)60 bool RtspCommon::VerifyMethod(const std::string &method)
61 {
62     return method == RTSP_METHOD_OPTIONS || method == RTSP_METHOD_DESCRIBE || method == RTSP_METHOD_ANNOUNCE ||
63            method == RTSP_METHOD_SETUP || method == RTSP_METHOD_PLAY || method == RTSP_METHOD_PAUSE ||
64            method == RTSP_METHOD_TEARDOWN || method == RTSP_METHOD_GET_PARAMETER ||
65            method == RTSP_METHOD_SET_PARAMETER || method == RTSP_METHOD_REDIRECT || method == RTSP_METHOD_RECORD;
66 }
67 
Trim(const std::string &str)68 std::string RtspCommon::Trim(const std::string &str)
69 {
70     if (str.empty())
71         return str;
72     auto s = str.find_first_not_of(' ');
73     auto e = str.find_last_not_of(' ');
74     if (s == std::string::npos || e == std::string::npos) {
75         return str;
76     }
77 
78     return str.substr(s, e + 1 - s);
79 }
80 
ParseMessage(const std::string &message, std::vector<std::string> &firstLine, std::unordered_map<std::string, std::string> &header, std::list<std::string> &body)81 RtspError RtspCommon::ParseMessage(const std::string &message, std::vector<std::string> &firstLine,
82                                    std::unordered_map<std::string, std::string> &header, std::list<std::string> &body)
83 {
84     auto messageV = RtspCommon::Split(message, std::string(RTSP_CRLF) + RTSP_CRLF);
85     auto headers = messageV[0];
86 
87     std::vector<std::string> lines = RtspCommon::Split(headers, RTSP_CRLF);
88     if (lines.size() < 2) { // 2:fixed size
89         return {RtspErrorType::INVALID_MESSAGE, "invalid message"};
90     }
91 
92     firstLine = RtspCommon::Split(lines[0], RTSP_SP);
93 
94     // parse header
95     for (int32_t i = 1; i < (int32_t)lines.size(); ++i) {
96         if (lines[i].size() > 3) { // 3:fixed size
97             auto index = lines[i].find_first_of(':');
98             if (index != 0 && index != std::string::npos && index + 1 != lines[i].length()) {
99                 auto token = RtspCommon::Trim(lines[i].substr(0, index));
100                 auto value = RtspCommon::Trim(lines[i].substr(index + 1));
101                 if (token == "WWW-Authenticate" && value.find("Digest") == std::string::npos) {
102                     continue;
103                 }
104                 header.emplace(token, value);
105             }
106         }
107     }
108 
109     // parse body
110     if (messageV.size() == 2 && header.find(RTSP_TOKEN_CONTENT_TYPE) != header.end() && // 2:fixed size
111         header.find(RTSP_TOKEN_CONTENT_LENGTH) != header.end()) {
112         int32_t length = atoi(header.at(RTSP_TOKEN_CONTENT_LENGTH).c_str());
113         if (length == 0) {
114             SHARING_LOGW("Content-Length == 0.");
115             return {};
116         }
117 
118         if (length > (int32_t)messageV[1].length()) {
119             SHARING_LOGE("invalid body: Content-Length: %{public}d, body length: %{public}zu\n!", length,
120                          messageV[1].length());
121             return {RtspErrorType::INCOMPLETE_MESSAGE, "body length < Content-Length"};
122         }
123 
124         if (header.at(RTSP_TOKEN_CONTENT_TYPE).compare(0, 5, "text/") != 0 && // 5:fixed size
125             header.at(RTSP_TOKEN_CONTENT_TYPE) != "application/sdp") {
126             return {RtspErrorType::INVALID_MESSAGE, "unsupported content"};
127         }
128 
129         auto bodyStr = messageV[1].substr(0, length);
130         auto bodyVec = RtspCommon::Split(bodyStr, RTSP_CRLF);
131         for (auto &item : bodyVec) {
132             if (!item.empty()) {
133                 body.emplace_back(item);
134             }
135         }
136     } else {
137         if (messageV.size() > 1) {
138             SHARING_LOGW("may packet splicing.");
139             std::string splicingPart;
140             for (int32_t i = 1; i < messageV.size(); ++i) {
141                 splicingPart += messageV[i];
142                 if (i != messageV.size() - 1) {
143                     splicingPart += "\r\n\r\n";
144                 }
145             }
146             splicingPart += '$';
147             SHARING_LOGW("splicing \n%{public}s!", splicingPart.c_str());
148             return {RtspErrorType::OK, splicingPart};
149         }
150     }
151 
152     return {};
153 }
GenerateAuthorization(const std::string &username, const std::string &realm, const std::string &password, const std::string &nonce, const std::string &method, const std::string &url)154 std::string RtspCommon::GenerateAuthorization(const std::string &username, const std::string &realm,
155                                               const std::string &password, const std::string &nonce,
156                                               const std::string &method, const std::string &url)
157 {
158     auto urpMD5 = GetMD5(username + ':' + realm + ':' + password);
159     auto pmuMD5 = GetMD5(method + url);
160     auto responseMD5 = GetMD5(urpMD5 + ':' + nonce + ':' + pmuMD5);
161     auto authorization = "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce +
162                          "\", uri=\"" + url + "\", response=\"" + responseMD5 + "\"";
163     return authorization;
164 }
165 } // namespace Sharing
166 } // namespace OHOS