1 /*
2 * Copyright (C) 2022 Huawei Device 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 "opp_obex_server.h"
17
18 #include <codecvt>
19 #include <dirent.h>
20 #include "../obex/obex_server.h"
21 #include "bt_def.h"
22 #include "log.h"
23 #include "opp_defines.h"
24 #include "opp_service.h"
25
26 namespace OHOS {
27 namespace bluetooth {
OppReceiveFileBodyObject(const std::string & file,const std::string address)28 OppReceiveFileBodyObject::OppReceiveFileBodyObject(const std::string &file, const std::string address)
29 {
30 address_ = address;
31 file_ = file;
32 OpenFile(file);
33 }
34
~OppReceiveFileBodyObject()35 OppReceiveFileBodyObject::~OppReceiveFileBodyObject()
36 {
37 try {
38 Close();
39 } catch(std::exception &e) {
40 LOG_ERROR("[OPP OBEX SERVER]%{public}s():Catch exception %{public}s", __FUNCTION__, e.what());
41 }
42 }
43
OpenFile(const std::string & file)44 void OppReceiveFileBodyObject::OpenFile(const std::string &file)
45 {
46 ofs_.open(file, std::ios::out);
47 if (!ofs_.is_open()) {
48 HILOGE("[OPP OBEX SERVER] file open failed");
49 return;
50 } else {
51 HILOGI("[OPP OBEX SERVER] file=%{public}s opened.", file.c_str());
52 }
53 }
54
Read(uint8_t * buf,size_t bufLen)55 size_t OppReceiveFileBodyObject::Read(uint8_t *buf, size_t bufLen)
56 {
57 return bufLen;
58 }
59
Write(const uint8_t * buf,size_t bufLen)60 size_t OppReceiveFileBodyObject::Write(const uint8_t *buf, size_t bufLen)
61 {
62 size_t writeSize = 0;
63 if (ofs_.is_open()) {
64 auto buffer = reinterpret_cast<const char *>(buf);
65 ofs_.write(buffer, bufLen);
66 fileReceiveSize_ += bufLen;
67 writeSize = bufLen;
68 OppService::GetService()->OnTransferPositionChange(address_, fileReceiveSize_);
69 HILOGI("[OPP OBEX SERVER] write file bufLen=%zu", bufLen);
70 } else {
71 OppService::GetService()->CancelTransfer(address_);
72 HILOGE("[OPP OBEX SERVER] file open failed");
73 }
74 return writeSize;
75 }
76
SetFileReceiveSuccess()77 void OppReceiveFileBodyObject::SetFileReceiveSuccess()
78 {
79 fileReceiveSuccess_ = true;
80 }
81
Close()82 int OppReceiveFileBodyObject::Close()
83 {
84 ofs_.close();
85 if (!fileReceiveSuccess_) {
86 remove(file_.c_str());
87 }
88 return RET_NO_ERROR;
89 }
90
OppObexServer(const ObexServerConfig & config,utility::Dispatcher & dispatcher)91 OppObexServer::OppObexServer(const ObexServerConfig &config, utility::Dispatcher &dispatcher)
92 {
93 HILOGI("[OPP OBEX SERVER] Enter");
94 oppObexObserver_ = std::make_unique<OppObexObserver>();
95 obexServer_ = std::make_unique<ObexServer>(OPP_SERVICE_NAME, config, *oppObexObserver_, dispatcher);
96 }
97
StartUp() const98 int OppObexServer::StartUp() const
99 {
100 HILOGI("[OPP OBEX SERVER] Enter");
101 int ret = obexServer_->Startup();
102 if (ret != RET_NO_ERROR) {
103 HILOGE("[OPP OBEX SERVER]:Obex Startup Error ret = %{public}d", ret);
104 }
105 return ret;
106 }
107
ShutDown() const108 int OppObexServer::ShutDown() const
109 {
110 HILOGI("[OPP OBEX SERVER] Enter");
111 obexServer_->Shutdown();
112 return RET_NO_ERROR;
113 }
114
SendOppDisconnected(const std::string & btAddr) const115 void OppObexServer::OppObexObserver::SendOppDisconnected(const std::string &btAddr) const
116 {
117 OppMessage event(OPP_DISCONNECTED_EVT);
118 event.dev_ = btAddr;
119 OppService::GetService()->PostEvent(event);
120 }
121
OnTransportDisconnected(const std::string & btAddr)122 void OppObexServer::OppObexObserver::OnTransportDisconnected(const std::string &btAddr)
123 {
124 HILOGI("[OPP OBEX SERVER] Enter");
125 SendOppDisconnected(btAddr);
126 }
127
OnTransportError(const std::string & btAddr,int errCd,const std::string & msg)128 void OppObexServer::OppObexObserver::OnTransportError(const std::string &btAddr, int errCd, const std::string &msg)
129 {
130 HILOGI("[OPP OBEX SERVER] Enter");
131 SendOppDisconnected(btAddr);
132 }
133
OnTransportConnect(ObexIncomingConnect & incomingConnect)134 void OppObexServer::OppObexObserver::OnTransportConnect(ObexIncomingConnect &incomingConnect)
135 {
136 HILOGI("[OPP OBEX SERVER] Enter");
137 incomingConnect.AcceptConnection();
138 }
139
OnConnect(ObexServerSession & session,const ObexHeader & req)140 void OppObexServer::OppObexObserver::OnConnect(ObexServerSession &session, const ObexHeader &req)
141 {
142 HILOGI("[OPP OBEX SERVER] Enter");
143 OppService::GetService()->OnReceiveIncomingConnect(session, connectionId_);
144 }
145
OnDisconnect(ObexServerSession & session,const ObexHeader & req)146 void OppObexServer::OppObexObserver::OnDisconnect(ObexServerSession &session, const ObexHeader &req)
147 {
148 HILOGI("[OPP OBEX SERVER] Enter");
149 auto header = ObexHeader::CreateResponse(ObexRspCode::SUCCESS, false);
150 auto connectId = req.GetItemConnectionId();
151 if (connectId != nullptr) {
152 header->AppendItemConnectionId(connectId->GetWord());
153 }
154 session.SendResponse(*header);
155 session.Disconnect();
156 }
157
OnPut(ObexServerSession & session,const ObexHeader & req)158 void OppObexServer::OppObexObserver::OnPut(ObexServerSession &session, const ObexHeader &req)
159 {
160 HILOGI("[OPP OBEX SERVER] Enter");
161 bool isHeader = false;
162 int ret = RET_NO_ERROR;
163 if (session.GetReceivedObject() == nullptr) {
164 ret = ReceiveFileHeader(session, req);
165 isHeader = true;
166 }
167 if (ret == RET_NO_ERROR) {
168 ReceiveFileBody(session, req, isHeader);
169 }
170 }
171
OnAbort(ObexServerSession & session,const ObexHeader & req)172 void OppObexServer::OppObexObserver::OnAbort(ObexServerSession &session, const ObexHeader &req)
173 {
174 HILOGI("[OPP OBEX SERVER] Enter");
175 auto header = ObexHeader::CreateResponse(ObexRspCode::SUCCESS, false);
176 auto connectId = req.GetItemConnectionId();
177 if (connectId != nullptr) {
178 header->AppendItemConnectionId(connectId->GetWord());
179 }
180 session.SendResponse(*header);
181 OppService::GetService()->OnTransferStateChange(
182 session.GetRemoteAddr().GetAddress(), OPP_TRANSFER_STATUS_FAILD, OPP_TRANSFER_FAILED_CANCELED);
183 }
184
OnBusy(ObexServerSession & session,bool isBusy) const185 void OppObexServer::OppObexObserver::OnBusy(ObexServerSession &session, bool isBusy) const
186 {
187 HILOGI("[OPP OBEX SERVER] Enter");
188 }
189
ReceiveFileHeader(ObexServerSession & session,const ObexHeader & req)190 int OppObexServer::OppObexObserver::ReceiveFileHeader(ObexServerSession &session, const ObexHeader &req)
191 {
192 auto nameItem = req.GetItemName();
193 std::u16string name;
194 if (nameItem != nullptr) {
195 name = nameItem->GetUnicodeText();
196 }
197 if (name.empty()) {
198 HILOGE("[OPP OBEX SERVER]:name is empty");
199 session.SendSimpleResponse(ObexRspCode::BAD_REQUEST);
200 return RET_BAD_STATUS;
201 }
202 std::string fileName = U16stringToString(name);
203 if (NeedRejectFileForPts(fileName)) {
204 HILOGE("[OPP OBEX SERVER]:name is pts reject");
205 session.SendSimpleResponse(ObexRspCode::UNSUPPORTED_MEDIA_TYPE);
206 return RET_BAD_STATUS;
207 }
208
209 auto typeItem = req.GetItemType();
210 std::string type;
211 if (typeItem != nullptr) {
212 type = typeItem->GetString();
213 }
214
215 auto lengthItem = req.GetItemLength();
216 size_t length = 0;
217 if (lengthItem != nullptr) {
218 length = lengthItem->GetWord();
219 }
220 if (length <= 0) {
221 HILOGE("[OPP OBEX SERVER]:length is 0");
222 session.SendSimpleResponse(ObexRspCode::LENGTH_REQUIRED);
223 return RET_BAD_STATUS;
224 }
225
226 std::string filePath = OPP_RECEIVE_FILE_PATH + RenameFile(fileName);
227
228 std::shared_ptr<ObexBodyObject> writer =
229 std::make_shared<OppReceiveFileBodyObject>(filePath, session.GetRemoteAddr().GetAddress());
230 std::unique_ptr<ObexServerReceivedObject> &receivedObject = session.CreateReceivedObject(req, writer);
231 if ((receivedObject != nullptr) && session.IsSupportSrmMode() && req.GetItemSrm()) {
232 receivedObject->SetSrmEnable(true);
233 }
234
235 IOppTransferInformation info;
236 info.SetDeviceAddress(session.GetRemoteAddr().GetAddress());
237 info.SetFileName(fileName);
238 info.SetFilePath(filePath);
239 info.SetFileType(type);
240 info.SetTotalBytes(static_cast<uint64_t>(length));
241 OppService::GetService()->OnReceiveIncomingFile(info);
242 return RET_NO_ERROR;
243 }
244
245 // For PTS OPP/SR/OPH/BV-xxx.
NeedRejectFileForPts(std::string fileName) const246 bool OppObexServer::OppObexObserver::NeedRejectFileForPts(std::string fileName) const
247 {
248 // regect OPHBV10,OPHBV14,OPHBV18.
249 std::string ptsRejectName = "OPHBV";
250 std::string::size_type idx = fileName.find(ptsRejectName);
251 if (idx == fileName.npos) {
252 return false;
253 }
254 return true;
255 }
256
ReceiveFileBody(ObexServerSession & session,const ObexHeader & req,bool isHead) const257 void OppObexServer::OppObexObserver::ReceiveFileBody(ObexServerSession &session,
258 const ObexHeader &req, bool isHead) const
259 {
260 std::unique_ptr<ObexServerReceivedObject> &receivedObject = session.GetReceivedObject();
261 if (receivedObject == nullptr) {
262 session.SendSimpleResponse(ObexRspCode::BAD_REQUEST);
263 OppService::GetService()->OnTransferStateChange(
264 session.GetRemoteAddr().GetAddress(), OPP_TRANSFER_STATUS_FAILD, OPP_TRANSFER_FAILED_PROTOCOL);
265 return;
266 }
267 auto body = req.GetItemBody();
268 if (body != nullptr) {
269 HILOGI("[OPP OBEX SERVER]:Server Received Body From Request!");
270 receivedObject->AppendBody(body->GetBytes().get(), body->GetHeaderDataSize());
271 }
272 if (req.GetFieldCode() == static_cast<uint8_t>(ObexOpeId::PUT_FINAL)) {
273 auto fbody = req.GetItemEndBody();
274 if (fbody != nullptr) {
275 HILOGI("[OPP OBEX SERVER]:Server Received End-Body From Request!");
276 receivedObject->AppendBody(fbody->GetBytes().get(), fbody->GetHeaderDataSize());
277 }
278 auto header = ObexHeader::CreateResponse(ObexRspCode::SUCCESS, false);
279 auto connectId = req.GetItemConnectionId();
280 if (connectId != nullptr) {
281 header->AppendItemConnectionId(connectId->GetWord());
282 }
283 header->AppendItemEndBody(nullptr, 0);
284 session.SendResponse(*header);
285
286 OppReceiveFileBodyObject *bodyObject = static_cast<OppReceiveFileBodyObject *>(
287 receivedObject->GetWriter().get());
288 if (bodyObject != nullptr) {
289 bodyObject->SetFileReceiveSuccess();
290 session.FreeReceivedObject();
291 OppService::GetService()->OnTransferStateChange(
292 session.GetRemoteAddr().GetAddress(), OPP_TRANSFER_STATUS_SUCCESS, 0);
293 return;
294 }
295 session.FreeReceivedObject();
296 OppService::GetService()->OnTransferStateChange(
297 session.GetRemoteAddr().GetAddress(), OPP_TRANSFER_STATUS_FAILD, OPP_TRANSFER_FAILED_PROTOCOL);
298 } else if (!isHead) {
299 if (!session.IsSupportSrmMode() || req.GetItemSrm()) {
300 auto respHeader = ObexHeader::CreateResponse(ObexRspCode::CONTINUE);
301 auto connectId = req.GetItemConnectionId();
302 if (connectId != nullptr) {
303 respHeader->AppendItemConnectionId(connectId->GetWord());
304 }
305 if (session.IsSupportSrmMode() && req.GetItemSrm()) {
306 respHeader->AppendItemSrm(true);
307 }
308 session.SendResponse(*respHeader);
309 }
310 }
311 }
312
U16stringToString(const std::u16string & u16str) const313 std::string OppObexServer::OppObexObserver::U16stringToString(const std::u16string &u16str) const
314 {
315 return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(u16str);
316 }
317
RenameFile(std::string fileName) const318 std::string OppObexServer::OppObexObserver::RenameFile(std::string fileName) const
319 {
320 if (!HasSameName(OPP_RECEIVE_FILE_PATH, fileName)) {
321 return fileName;
322 }
323
324 time_t timeStamp = 0;
325 (void)time(&timeStamp);
326 char *name = strchr(&fileName[0], '.');
327 if (name == nullptr) {
328 HILOGI("[OPP OBEX SERVER] opendir error");
329 return fileName;
330 }
331 size_t namelen = name - &fileName[0];
332 std::string rename = fileName.substr(0, namelen);
333 std::string type = fileName.erase(0, fileName.find_last_of('.') + 1);
334 std::string fileNewName = rename + "_" + std::to_string(timeStamp) + "." + type;
335 return fileNewName;
336 }
337
HasSameName(std::string path,std::string name) const338 bool OppObexServer::OppObexObserver::HasSameName(std::string path, std::string name) const
339 {
340 DIR *dir = opendir(path.c_str());
341 if (dir == nullptr) {
342 HILOGI("[OPP OBEX SERVER] opendir error");
343 return false;
344 }
345
346 struct dirent *ent;
347 while ((ent = readdir(dir)) != nullptr) {
348 if (strcmp(ent->d_name, name.c_str()) == 0) {
349 HILOGI("[OPP OBEX SERVER] has same name");
350 return true;
351 }
352 }
353 return false;
354 }
355 } // namespace bluetooth
356 } // namespace OHOS
357