mumlib.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. #include "mumlib/CryptState.hpp"
  2. #include "mumlib/VarInt.hpp"
  3. #include "mumlib/enums.hpp"
  4. #include "mumlib/Transport.hpp"
  5. #include "mumlib/Audio.hpp"
  6. #include "mumlib.hpp"
  7. #include <boost/asio.hpp>
  8. #include <boost/bind.hpp>
  9. #include <openssl/sha.h>
  10. #include <log4cpp/Category.hh>
  11. #include <Mumble.pb.h>
  12. using namespace std;
  13. using namespace boost::asio;
  14. using namespace mumlib;
  15. namespace mumlib {
  16. struct _Mumlib_Private : boost::noncopyable {
  17. log4cpp::Category &logger = log4cpp::Category::getInstance("mumlib.Mumlib");
  18. bool externalIoService;
  19. io_service &ioService;
  20. Callback &callback;
  21. Transport transport;
  22. Audio audio;
  23. int sessionId = 0;
  24. int channelId = 0;
  25. int64_t seq = 0;
  26. std::vector<MumbleUser> listMumbleUser;
  27. std::vector<MumbleChannel> listMumbleChannel;
  28. _Mumlib_Private(Callback &callback, MumlibConfiguration &configuration)
  29. : _Mumlib_Private(callback, *(new io_service()), configuration) {
  30. externalIoService = false;
  31. }
  32. _Mumlib_Private(
  33. Callback &callback,
  34. io_service &ioService,
  35. MumlibConfiguration &configuration)
  36. : callback(callback),
  37. ioService(ioService),
  38. externalIoService(true),
  39. transport(
  40. ioService,
  41. boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3),
  42. boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3),
  43. false,
  44. configuration.cert_file,
  45. configuration.privkey_file),
  46. audio(configuration.opusSampleRate, configuration.opusEncoderBitrate, configuration.opusChannels) {
  47. audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate);
  48. }
  49. virtual ~_Mumlib_Private() {
  50. if (not externalIoService) {
  51. delete &ioService;
  52. }
  53. }
  54. bool processAudioPacket(AudioPacketType type, uint8_t *buffer, int length) {
  55. logger.info("Got %d B of encoded audio data.", length);
  56. try {
  57. auto incomingAudioPacket = audio.decodeIncomingAudioPacket(buffer, length);
  58. if (type == AudioPacketType::OPUS) {
  59. int16_t pcmData[5000];
  60. audio.addFrameToBuffer(incomingAudioPacket.audioPayload,
  61. incomingAudioPacket.audioPayloadLength,
  62. seq);
  63. auto status = audio.decodeOpusPayload(pcmData, 5000);
  64. // auto status = audio.decodeOpusPayload(incomingAudioPacket.audioPayload,
  65. // incomingAudioPacket.audioPayloadLength,
  66. // pcmData,
  67. // 5000);
  68. if(status.second) seq = 0; else seq++;
  69. callback.audio(incomingAudioPacket.target,
  70. incomingAudioPacket.sessionId,
  71. incomingAudioPacket.sequenceNumber,
  72. pcmData,
  73. status.first);
  74. } else {
  75. logger.warn("Incoming audio packet doesn't contain Opus data, calling unsupportedAudio callback.");
  76. callback.unsupportedAudio(incomingAudioPacket.target,
  77. incomingAudioPacket.sessionId,
  78. incomingAudioPacket.sequenceNumber,
  79. incomingAudioPacket.audioPayload,
  80. incomingAudioPacket.audioPayloadLength);
  81. }
  82. } catch (mumlib::AudioException &exp) {
  83. logger.error("Audio decode error: %s.", exp.what());
  84. }
  85. return true;
  86. }
  87. private:
  88. bool processIncomingTcpMessage(MessageType messageType, uint8_t *buffer, int length) {
  89. logger.debug("Process incoming message: type %d, length: %d.", messageType, length);
  90. switch (messageType) {
  91. case MessageType::VERSION: {
  92. MumbleProto::Version version;
  93. version.ParseFromArray(buffer, length);
  94. callback.version(
  95. version.version() >> 16,
  96. version.version() >> 8 & 0xff,
  97. version.version() & 0xff,
  98. version.release(),
  99. version.os(),
  100. version.os_version());
  101. }
  102. break;
  103. case MessageType::SERVERSYNC: {
  104. MumbleProto::ServerSync serverSync;
  105. serverSync.ParseFromArray(buffer, length);
  106. sessionId = serverSync.session();
  107. callback.serverSync(
  108. serverSync.welcome_text(),
  109. serverSync.session(),
  110. serverSync.max_bandwidth(),
  111. serverSync.permissions()
  112. );
  113. }
  114. break;
  115. case MessageType::CHANNELREMOVE: {
  116. MumbleProto::ChannelRemove channelRemove;
  117. channelRemove.ParseFromArray(buffer, length);
  118. if(isListChannelContains(channelRemove.channel_id())) {
  119. listChannelRemovedBy(channelRemove.channel_id());
  120. }
  121. callback.channelRemove(channelRemove.channel_id());
  122. }
  123. break;
  124. case MessageType::CHANNELSTATE: {
  125. MumbleProto::ChannelState channelState;
  126. channelState.ParseFromArray(buffer, length);
  127. int32_t channel_id = channelState.has_channel_id() ? channelState.channel_id() : -1;
  128. int32_t parent = channelState.has_parent() ? channelState.parent() : -1;
  129. bool temporary = channelState.has_temporary() ? channelState.temporary()
  130. : false; //todo make sure it's correct to assume it's false
  131. int position = channelState.has_position() ? channelState.position() : 0;
  132. vector<uint32_t> links;
  133. for (int i = 0; i < channelState.links_size(); ++i) {
  134. links.push_back(channelState.links(i));
  135. }
  136. vector<uint32_t> links_add;
  137. for (int i = 0; i < channelState.links_add_size(); ++i) {
  138. links_add.push_back(channelState.links_add(i));
  139. }
  140. vector<uint32_t> links_remove;
  141. for (int i = 0; i < channelState.links_remove_size(); ++i) {
  142. links_remove.push_back(channelState.links_remove(i));
  143. }
  144. MumbleChannel mumbleChannel;
  145. mumbleChannel.channelId = channel_id;
  146. mumbleChannel.name = channelState.name();
  147. mumbleChannel.description = channelState.description();
  148. if(not isListChannelContains(channel_id)) {
  149. listMumbleChannel.push_back(mumbleChannel);
  150. }
  151. callback.channelState(
  152. channelState.name(),
  153. channel_id,
  154. parent,
  155. channelState.description(),
  156. links,
  157. links_add,
  158. links_remove,
  159. temporary,
  160. position
  161. );
  162. }
  163. break;
  164. case MessageType::USERREMOVE: {
  165. MumbleProto::UserRemove user_remove;
  166. user_remove.ParseFromArray(buffer, length);
  167. int32_t actor = user_remove.has_actor() ? user_remove.actor() : -1;
  168. bool ban = user_remove.has_ban() ? user_remove.ban()
  169. : false; //todo make sure it's correct to assume it's false
  170. if(isListUserContains(user_remove.session())) {
  171. listUserRemovedBy(user_remove.session());
  172. }
  173. callback.userRemove(
  174. user_remove.session(),
  175. actor,
  176. user_remove.reason(),
  177. ban
  178. );
  179. }
  180. break;
  181. case MessageType::USERSTATE: {
  182. MumbleProto::UserState userState;
  183. userState.ParseFromArray(buffer, length);
  184. // There are far too many things in this structure. Culling to the ones that are probably important
  185. int32_t session = userState.has_session() ? userState.session() : -1;
  186. int32_t actor = userState.has_actor() ? userState.actor() : -1;
  187. int32_t user_id = userState.has_user_id() ? userState.user_id() : -1;
  188. int32_t channel_id = userState.has_channel_id() ? userState.channel_id() : -1;
  189. int32_t mute = userState.has_mute() ? userState.mute() : -1;
  190. int32_t deaf = userState.has_deaf() ? userState.deaf() : -1;
  191. int32_t suppress = userState.has_suppress() ? userState.suppress() : -1;
  192. int32_t self_mute = userState.has_self_mute() ? userState.self_mute() : -1;
  193. int32_t self_deaf = userState.has_self_deaf() ? userState.self_deaf() : -1;
  194. int32_t priority_speaker = userState.has_priority_speaker() ? userState.priority_speaker() : -1;
  195. int32_t recording = userState.has_recording() ? userState.recording() : -1;
  196. if(session == this->sessionId) {
  197. this->channelId = channel_id;
  198. }
  199. MumbleUser mumbleUser;
  200. mumbleUser.name = userState.name();
  201. mumbleUser.sessionId = session;
  202. if(not isListUserContains(session)) {
  203. listMumbleUser.push_back(mumbleUser);
  204. }
  205. callback.userState(session,
  206. actor,
  207. userState.name(),
  208. user_id,
  209. channel_id,
  210. mute,
  211. deaf,
  212. suppress,
  213. self_mute,
  214. self_deaf,
  215. userState.comment(),
  216. priority_speaker,
  217. recording);
  218. }
  219. break;
  220. case MessageType::BANLIST: {
  221. MumbleProto::BanList ban_list;
  222. ban_list.ParseFromArray(buffer, length);
  223. for (int i = 0; i < ban_list.bans_size(); i++) {
  224. auto ban = ban_list.bans(i);
  225. const uint8_t *ip_data = reinterpret_cast<const uint8_t *>(ban.address().c_str());
  226. uint32_t ip_data_size = ban.address().size();
  227. int32_t duration = ban.has_duration() ? ban.duration() : -1;
  228. callback.banList(
  229. ip_data,
  230. ip_data_size,
  231. ban.mask(),
  232. ban.name(),
  233. ban.hash(),
  234. ban.reason(),
  235. ban.start(),
  236. duration);
  237. }
  238. }
  239. break;
  240. case MessageType::TEXTMESSAGE: {
  241. MumbleProto::TextMessage text_message;
  242. text_message.ParseFromArray(buffer, length);
  243. int32_t actor = text_message.has_actor() ? text_message.actor() : -1;
  244. vector<uint32_t> sessions;
  245. for (int i = 0; i < text_message.session_size(); ++i) {
  246. sessions.push_back(text_message.session(i));
  247. }
  248. vector<uint32_t> channel_ids;
  249. for (int i = 0; i < text_message.channel_id_size(); ++i) {
  250. channel_ids.push_back(text_message.channel_id(i));
  251. }
  252. vector<uint32_t> tree_ids;
  253. for (int i = 0; i < text_message.tree_id_size(); ++i) {
  254. tree_ids.push_back(text_message.tree_id(i));
  255. }
  256. callback.textMessage(actor, sessions, channel_ids, tree_ids, text_message.message());
  257. }
  258. break;
  259. case MessageType::PERMISSIONDENIED: // 12
  260. logger.warn("PermissionDenied Message: support not implemented yet");
  261. break;
  262. case MessageType::ACL: // 13
  263. logger.warn("ACL Message: support not implemented yet.");
  264. break;
  265. case MessageType::QUERYUSERS: // 14
  266. logger.warn("QueryUsers Message: support not implemented yet");
  267. break;
  268. case MessageType::CONTEXTACTIONMODIFY: // 16
  269. logger.warn("ContextActionModify Message: support not implemented yet");
  270. break;
  271. case MessageType::CONTEXTACTION: // 17
  272. logger.warn("ContextAction Message: support not implemented yet");
  273. break;
  274. case MessageType::USERLIST: // 18
  275. logger.warn("UserList Message: support not implemented yet");
  276. break;
  277. case MessageType::VOICETARGET:
  278. logger.warn("VoiceTarget Message: I don't think the server ever sends this structure.");
  279. break;
  280. case MessageType::PERMISSIONQUERY: {
  281. MumbleProto::PermissionQuery permissionQuery;
  282. permissionQuery.ParseFromArray(buffer, length);
  283. int32_t channel_id = permissionQuery.has_channel_id() ? permissionQuery.channel_id() : -1;
  284. uint32_t permissions = permissionQuery.has_permissions() ? permissionQuery.permissions() : 0;
  285. uint32_t flush = permissionQuery.has_flush() ? permissionQuery.flush() : -1;
  286. callback.permissionQuery(channel_id, permissions, flush);
  287. }
  288. break;
  289. case MessageType::CODECVERSION: {
  290. MumbleProto::CodecVersion codecVersion;
  291. codecVersion.ParseFromArray(buffer, length);
  292. int32_t alpha = codecVersion.alpha();
  293. int32_t beta = codecVersion.beta();
  294. uint32_t prefer_alpha = codecVersion.prefer_alpha();
  295. int32_t opus = codecVersion.has_opus() ? codecVersion.opus() : 0;
  296. callback.codecVersion(alpha, beta, prefer_alpha, opus);
  297. }
  298. break;
  299. case MessageType::USERSTATS:
  300. logger.warn("UserStats Message: support not implemented yet");
  301. break;
  302. case MessageType::REQUESTBLOB: // 23
  303. logger.warn("RequestBlob Message: I don't think this is sent by the server.");
  304. break;
  305. case MessageType::SERVERCONFIG: {
  306. MumbleProto::ServerConfig serverConfig;
  307. serverConfig.ParseFromArray(buffer, length);
  308. uint32_t max_bandwidth = serverConfig.has_max_bandwidth() ? serverConfig.max_bandwidth() : 0;
  309. uint32_t allow_html = serverConfig.has_allow_html() ? serverConfig.allow_html() : 0;
  310. uint32_t message_length = serverConfig.has_message_length() ? serverConfig.message_length() : 0;
  311. uint32_t image_message_length = serverConfig.has_image_message_length()
  312. ? serverConfig.image_message_length() : 0;
  313. callback.serverConfig(max_bandwidth, serverConfig.welcome_text(), allow_html, message_length,
  314. image_message_length);
  315. }
  316. break;
  317. case MessageType::SUGGESTCONFIG: // 25
  318. logger.warn("SuggestConfig Message: support not implemented yet");
  319. break;
  320. default:
  321. throw MumlibException("unknown message type: " + to_string(static_cast<int>(messageType)));
  322. }
  323. return true;
  324. }
  325. bool isListUserContains(int sessionId) {
  326. for(int i = 0; i < listMumbleUser.size(); i++)
  327. if(listMumbleUser[i].sessionId == sessionId)
  328. return true;
  329. return false;
  330. }
  331. void listUserRemovedBy(int sessionId) {
  332. for(int i = 0; i < listMumbleUser.size(); i++) {
  333. if(listMumbleUser[i].sessionId == sessionId) {
  334. listMumbleUser.erase(listMumbleUser.begin() + i);
  335. return;
  336. }
  337. }
  338. }
  339. bool isListChannelContains(int channelId) {
  340. for(int i = 0; i < listMumbleChannel.size(); i++)
  341. if(listMumbleChannel[i].channelId == channelId)
  342. return true;
  343. return false;
  344. }
  345. void listChannelRemovedBy(int channelId) {
  346. for(int i = 0; i < listMumbleChannel.size(); i++) {
  347. if(listMumbleChannel[i].channelId == channelId) {
  348. listMumbleChannel.erase(listMumbleChannel.begin() + i);
  349. return;
  350. }
  351. }
  352. }
  353. };
  354. Mumlib::Mumlib(Callback &callback) {
  355. MumlibConfiguration conf;
  356. impl = new _Mumlib_Private(callback, conf);
  357. }
  358. Mumlib::Mumlib(Callback &callback, io_service &ioService) {
  359. MumlibConfiguration conf;
  360. impl = new _Mumlib_Private(callback, ioService, conf);
  361. }
  362. Mumlib::Mumlib(Callback &callback, MumlibConfiguration &configuration)
  363. : impl(new _Mumlib_Private(callback, configuration)) { }
  364. Mumlib::Mumlib(Callback &callback, io_service &ioService, MumlibConfiguration &configuration)
  365. : impl(new _Mumlib_Private(callback, ioService, configuration)) { }
  366. Mumlib::~Mumlib() {
  367. disconnect();
  368. delete impl;
  369. }
  370. ConnectionState Mumlib::getConnectionState() {
  371. return impl->transport.getConnectionState();
  372. }
  373. int Mumlib::getChannelId() {
  374. return impl->channelId;
  375. }
  376. vector<mumlib::MumbleUser> Mumlib::getListAllUser() {
  377. return impl->listMumbleUser;
  378. }
  379. vector<mumlib::MumbleChannel> Mumlib::getListAllChannel() {
  380. return impl->listMumbleChannel;
  381. }
  382. void Mumlib::connect(string host, int port, string user, string password) {
  383. impl->transport.connect(host, port, user, password);
  384. }
  385. void Mumlib::disconnect() {
  386. if (not impl->externalIoService) {
  387. impl->ioService.stop();
  388. }
  389. if (impl->transport.getConnectionState() != ConnectionState::NOT_CONNECTED) {
  390. impl->transport.disconnect();
  391. }
  392. }
  393. void Mumlib::run() {
  394. if (impl->externalIoService) {
  395. throw MumlibException("can't call run() when using external io_service");
  396. }
  397. impl->ioService.run();
  398. }
  399. void Mumlib::sendAudioData(int16_t *pcmData, int pcmLength) {
  400. Mumlib::sendAudioDataTarget(0, pcmData, pcmLength);
  401. }
  402. void Mumlib::sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength) {
  403. uint8_t encodedData[5000];
  404. int length = impl->audio.encodeAudioPacket(targetId, pcmData, pcmLength, encodedData, 5000);
  405. impl->transport.sendEncodedAudioPacket(encodedData, length);
  406. }
  407. void Mumlib::sendTextMessage(string message) {
  408. MumbleProto::TextMessage textMessage;
  409. textMessage.set_actor(impl->sessionId);
  410. textMessage.add_channel_id(impl->channelId);
  411. textMessage.set_message(message);
  412. impl->transport.sendControlMessage(MessageType::TEXTMESSAGE, textMessage);
  413. }
  414. void Mumlib::joinChannel(int channelId) {
  415. if(!isChannelIdValid(channelId)) // when channel has not been registered / create
  416. return;
  417. MumbleProto::UserState userState;
  418. userState.set_channel_id(channelId);
  419. impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
  420. impl->channelId = channelId;
  421. }
  422. void Mumlib::joinChannel(string name) {
  423. int channelId = Mumlib::getChannelIdBy(name);
  424. Mumlib::joinChannel(channelId);
  425. }
  426. void Mumlib::sendVoiceTarget(int targetId, VoiceTargetType type, int id) {
  427. MumbleProto::VoiceTarget voiceTarget;
  428. MumbleProto::VoiceTarget_Target voiceTargetTarget;
  429. switch(type) {
  430. case VoiceTargetType::CHANNEL: {
  431. voiceTargetTarget.set_channel_id(id);
  432. voiceTargetTarget.set_children(true);
  433. }
  434. break;
  435. case VoiceTargetType::USER: {
  436. voiceTargetTarget.add_session(id);
  437. }
  438. break;
  439. default:
  440. return;
  441. }
  442. voiceTarget.set_id(targetId);
  443. voiceTarget.add_targets()->CopyFrom(voiceTargetTarget);
  444. impl->transport.sendControlMessage(MessageType::VOICETARGET, voiceTarget);
  445. }
  446. void Mumlib::sendVoiceTarget(int targetId, VoiceTargetType type, string name, int &error) {
  447. int id;
  448. switch(type) {
  449. case VoiceTargetType::CHANNEL:
  450. id = getChannelIdBy(name);
  451. break;
  452. case VoiceTargetType::USER:
  453. id = getUserIdBy(name);
  454. break;
  455. default:
  456. break;
  457. }
  458. error = id < 0 ? 1: 0;
  459. if(error) return;
  460. sendVoiceTarget(targetId, type, id);
  461. }
  462. void Mumlib::sendUserState(mumlib::UserState field, bool val) {
  463. MumbleProto::UserState userState;
  464. switch (field) {
  465. case UserState::MUTE:
  466. userState.set_mute(val);
  467. break;
  468. case UserState::DEAF:
  469. userState.set_deaf(val);
  470. break;
  471. case UserState::SUPPRESS:
  472. userState.set_suppress(val);
  473. break;
  474. case UserState::SELF_MUTE:
  475. userState.set_self_mute(val);
  476. break;
  477. case UserState::SELF_DEAF:
  478. userState.set_self_deaf(val);
  479. break;
  480. case UserState::PRIORITY_SPEAKER:
  481. userState.set_priority_speaker(val);
  482. break;
  483. case UserState::RECORDING:
  484. userState.set_recording(val);
  485. break;
  486. default:
  487. // in any other case, just ignore the command
  488. return;
  489. }
  490. impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
  491. }
  492. void Mumlib::sendUserState(mumlib::UserState field, std::string val) {
  493. MumbleProto::UserState userState;
  494. // if comment longer than 128 bytes, we need to set the SHA1 hash
  495. // http://www.askyb.com/cpp/openssl-sha1-hashing-example-in-cpp/
  496. unsigned char digest[SHA_DIGEST_LENGTH];
  497. char mdString[SHA_DIGEST_LENGTH * 2 + 1];
  498. SHA1((unsigned char*) val.c_str(), val.size(), digest);
  499. for(int i = 0; i < SHA_DIGEST_LENGTH; i++)
  500. sprintf(&mdString[i*2], "%02x", (unsigned int) digest[i]);
  501. switch (field) {
  502. case UserState::COMMENT:
  503. if(val.size() < 128)
  504. userState.set_comment(val);
  505. else
  506. userState.set_comment_hash(mdString);
  507. break;
  508. default:
  509. // in any other case, just ignore the command
  510. return;
  511. }
  512. impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
  513. }
  514. int Mumlib::getChannelIdBy(string name) {
  515. vector<mumlib::MumbleChannel> listMumbleChannel = impl->listMumbleChannel;
  516. for(int i = 0; i < listMumbleChannel.size(); i++)
  517. if(listMumbleChannel[i].name == name)
  518. return listMumbleChannel[i].channelId;
  519. return -1;
  520. }
  521. int Mumlib::getUserIdBy(string name) {
  522. vector<mumlib::MumbleUser> listMumbleUser = impl->listMumbleUser;
  523. for(int i = 0; i < listMumbleUser.size(); i++)
  524. if(listMumbleUser[i].name == name)
  525. return listMumbleUser[i].sessionId;
  526. return -1;
  527. }
  528. bool Mumlib::isSessionIdValid(int sessionId) {
  529. vector<mumlib::MumbleUser> listMumbleUser = impl->listMumbleUser;
  530. for(int i = 0; i < listMumbleUser.size(); i++)
  531. if(listMumbleUser[i].sessionId == sessionId)
  532. return true;
  533. return false;
  534. }
  535. bool Mumlib::isChannelIdValid(int channelId) {
  536. vector<mumlib::MumbleChannel> listMumbleChannel = impl->listMumbleChannel;
  537. for(int i = 0; i < listMumbleChannel.size(); i++)
  538. if(listMumbleChannel[i].channelId == channelId)
  539. return true;
  540. return false;
  541. }
  542. }