mumlib.cpp 25 KB

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