Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ make
Sample usage is covered in *mumlib_example.cpp* file. Basically, you should extend *mumlib::Callback* class
to implement your own handlers.

To use a client certificate, you'll need a PEM certificate and private key without a passphrase. These are assed in the MumlibConfig struct to the Mumlib object constructor. Support for passphrase still needs to be added.

## Credits

2015 Michał Słomkowski. The code is published under the terms of Lesser General Public License Version 3.

The library contains code from following 3rd party projects:

* official Mumble Client: https://github.com/mumble-voip/mumble
* *libmumble*: https://github.com/cornejo/libmumble
* *libmumble*: https://github.com/cornejo/libmumble
10 changes: 10 additions & 0 deletions include/mumlib.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace mumlib {

struct MumlibConfiguration {
int opusEncoderBitrate = DEFAULT_OPUS_ENCODER_BITRATE;
std::string cert_file = "";
std::string privkey_file = "";
// additional fields will be added in the future
};

Expand Down Expand Up @@ -54,6 +56,14 @@ namespace mumlib {

void joinChannel(int channelId);

void sendUserState(mumlib::UserState field, bool val);

void sendUserState(mumlib::UserState field, string val);

// These two are deprecated by sendUserState
void self_mute(int muteness);
void self_deaf(int deafness);

private:
_Mumlib_Private *impl;
};
Expand Down
24 changes: 23 additions & 1 deletion include/mumlib/Transport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,26 @@ namespace mumlib {
TransportException(string message) : MumlibException(message) { }
};

/* This helper is needed because the sslContext and sslSocket are initialized in
* the Transport constructor and there wasn't an easier way of passing these two
* arguments.
* TODO: add support for password callback.
*/
class SslContextHelper : boost::noncopyable {
public:
SslContextHelper(boost::asio::ssl::context &ctx,
std::string cert_file, std::string privkey_file);
~SslContextHelper() { };
};

class Transport : boost::noncopyable {
public:
Transport(io_service &ioService,
ProcessControlMessageFunction processControlMessageFunc,
ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction,
bool noUdp = false);
bool noUdp = false,
std::string cert_file = "",
std::string privkey_file = "");

~Transport();

Expand All @@ -47,6 +61,13 @@ namespace mumlib {
string user,
string password);

void connect(string host,
int port,
string user,
string password,
string cert_file,
string privkey_file);

void disconnect();

ConnectionState getConnectionState() {
Expand Down Expand Up @@ -84,6 +105,7 @@ namespace mumlib {
CryptState cryptState;

ssl::context sslContext;
SslContextHelper sslContextHelper;
ssl::stream<tcp::socket> sslSocket;
uint8_t *sslIncomingBuffer;

Expand Down
13 changes: 12 additions & 1 deletion include/mumlib/enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,16 @@ namespace mumlib {
CELT_Beta,
OPUS
};

enum class UserState {
MUTE,
DEAF,
SUPPRESS,
SELF_MUTE,
SELF_DEAF,
COMMENT,
PRIORITY_SPEAKER,
RECORDING
};

}
}
10 changes: 7 additions & 3 deletions mumlib_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ int main(int argc, char *argv[]) {
logger.setPriority(log4cpp::Priority::NOTICE);
logger.addAppender(appender1);

if (argc < 3) {
logger.crit("Usage: %s {server} {password}", argv[0]);
if (argc < 3 || argc == 4 || argc > 5) {
logger.crit("Usage: %s {server} {password} [{certfile} {keyfile}]", argv[0]);
return 1;
}

Expand All @@ -50,6 +50,10 @@ int main(int argc, char *argv[]) {
try {
mumlib::MumlibConfiguration conf;
conf.opusEncoderBitrate = 32000;
if ( argc > 3 && argc <= 5 ) {
conf.cert_file = argv[3];
conf.privkey_file = argv[4];
}
mumlib::Mumlib mum(myCallback, conf);
myCallback.mum = &mum;
mum.connect(argv[1], 64738, "mumlib_example", argv[2]);
Expand All @@ -61,4 +65,4 @@ int main(int argc, char *argv[]) {
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
}
}
20 changes: 17 additions & 3 deletions src/Transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ mumlib::Transport::Transport(
io_service &ioService,
mumlib::ProcessControlMessageFunction processMessageFunc,
ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction,
bool noUdp) :
bool noUdp,
std::string cert_file,
std::string privkey_file) :
logger(log4cpp::Category::getInstance("mumlib.Transport")),
ioService(ioService),
processMessageFunction(processMessageFunc),
Expand All @@ -38,6 +40,7 @@ mumlib::Transport::Transport(
state(ConnectionState::NOT_CONNECTED),
udpSocket(ioService),
sslContext(ssl::context::sslv23),
sslContextHelper(sslContext, cert_file, privkey_file),
sslSocket(ioService, sslContext),
pingTimer(ioService, PING_INTERVAL),
asyncBufferPool(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH)) {
Expand Down Expand Up @@ -67,6 +70,7 @@ void mumlib::Transport::connect(

sslSocket.set_verify_mode(boost::asio::ssl::verify_peer);


//todo for now it accepts every certificate, move it to callback
sslSocket.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context &ctx) {
return true;
Expand Down Expand Up @@ -520,17 +524,27 @@ void mumlib::Transport::throwTransportException(string message) {
throw TransportException(message);
}

mumlib::SslContextHelper::SslContextHelper(ssl::context &ctx, std::string cert_file, std::string privkey_file) {
if ( cert_file.size() > 0 ) {
ctx.use_certificate_file(cert_file, ssl::context::file_format::pem);
}
if ( privkey_file.size() > 0 ) {
ctx.use_private_key_file(privkey_file, ssl::context::file_format::pem);
}
}


void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) {
if (state != ConnectionState::CONNECTED) {
logger.warn("Connection not established.");
return;
}

if (udpActive) {
logger.info("Sending %d B of audio data via UDP.", length);
logger.debug("Sending %d B of audio data via UDP.", length);
sendUdpAsync(buffer, length);
} else {
logger.info("Sending %d B of audio data via TCP.", length);
logger.debug("Sending %d B of audio data via TCP.", length);

const uint16_t netUdptunnelType = htons(static_cast<uint16_t>(MessageType::UDPTUNNEL));

Expand Down
80 changes: 76 additions & 4 deletions src/mumlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,20 @@ namespace mumlib {
externalIoService = false;
}

_Mumlib_Private(Callback &callback, io_service &ioService, MumlibConfiguration &configuration)
_Mumlib_Private(
Callback &callback,
io_service &ioService,
MumlibConfiguration &configuration)
: callback(callback),
ioService(ioService),
externalIoService(true),
transport(ioService, boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3),
boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3)) {
transport(
ioService,
boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3),
boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3),
false,
configuration.cert_file,
configuration.privkey_file) {

audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate);
}
Expand Down Expand Up @@ -347,7 +355,7 @@ namespace mumlib {
impl = new _Mumlib_Private(callback, ioService, conf);
}

Mumlib::Mumlib(Callback &callback, MumlibConfiguration &configuration)
Mumlib::Mumlib(Callback &callback, MumlibConfiguration &configuration)
: impl(new _Mumlib_Private(callback, configuration)) { }

Mumlib::Mumlib(Callback &callback, io_service &ioService, MumlibConfiguration &configuration)
Expand Down Expand Up @@ -404,4 +412,68 @@ namespace mumlib {
impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
impl->channelId = channelId;
}

void Mumlib::sendUserState(mumlib::UserState field, bool val) {
MumbleProto::UserState userState;

switch (field) {
case UserState::MUTE:
userState.set_mute(val);
break;
case UserState::DEAF:
userState.set_deaf(val);
break;
case UserState::SUPPRESS:
userState.set_suppress(val);
break;
case UserState::SELF_MUTE:
userState.set_self_mute(val);
break;
case UserState::SELF_DEAF:
userState.set_self_deaf(val);
break;
case UserState::PRIORITY_SPEAKER:
userState.set_priority_speaker(val);
break;
case UserState::RECORDING:
userState.set_recording(val);
break;
default:
// in any other case, just ignore the command
return;
}

impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
}

void Mumlib::sendUserState(mumlib::UserState field, std::string val) {
MumbleProto::UserState userState;

switch (field) {
case UserState::COMMENT:
// TODO: if comment longer than 128 bytes, we need to set the SHA1 hash
userState.set_comment(val);
break;
default:
// in any other case, just ignore the command
return;
}

impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
}

// deprecated by sendUserState()
void Mumlib::self_mute(int muteness) {
MumbleProto::UserState userState;
userState.set_self_mute(muteness);
impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
}

// deprecated by sendUserState()
void Mumlib::self_deaf(int deafness) {
MumbleProto::UserState userState;
userState.set_self_deaf(deafness);
impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
}

}