mumlib.cpp 21 KB

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