1 /* Copyright (C) 2009-2010, Martin Johansson <martin@fatbob.nu>
2 Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 - Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 - Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15 - Neither the name of the Developers nor the names of its contributors may
16 be used to endorse or promote products derived from this software without
17 specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
23 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include "messagehandler.h"
42 #include "voicetarget.h"
46 extern channel_t *defaultChan;
47 extern int iCodecAlpha, iCodecBeta;
48 extern bool_t bPreferAlpha;
50 static void sendServerReject(client_t *client, const char *reason, MumbleProto__Reject__RejectType type)
52 message_t *msg = Msg_create(Reject);
53 msg->payload.reject->reason = strdup(reason);
54 msg->payload.reject->type = type;
55 msg->payload.reject->has_type = true;
56 Client_send_message(client, msg);
58 Log_info_client(client, "Server reject reason: %s", reason);
61 static void sendPermissionDenied(client_t *client, const char *reason)
63 message_t *msg = Msg_create(PermissionDenied);
64 msg->payload.permissionDenied->has_type = true;
65 msg->payload.permissionDenied->type = MUMBLE_PROTO__PERMISSION_DENIED__DENY_TYPE__Text;
66 msg->payload.permissionDenied->reason = strdup(reason);
67 Client_send_message(client, msg);
70 void Mh_handle_message(client_t *client, message_t *msg)
72 message_t *sendmsg = NULL;
73 channel_t *ch_itr = NULL;
76 if (!client->authenticated && !(msg->messageType == Authenticate ||
77 msg->messageType == Version)) {
81 switch (msg->messageType) {
90 Timer_restart(&client->idleTime);
93 switch (msg->messageType) {
95 Log_debug("Authenticate message received");
97 if (IS_AUTH(client) || !msg->payload.authenticate->username) {
98 /* Authenticate message might be sent when a token is set by the user.*/
99 if (msg->payload.authenticate->n_tokens > 0) {
100 Log_debug("Tokens in auth message from %s", client->username);
105 client->authenticated = true;
108 while (Client_iterate(&client_itr) != NULL) {
109 if (!IS_AUTH(client_itr))
111 if (client_itr->username && strncmp(client_itr->username, msg->payload.authenticate->username, MAX_TEXT) == 0) {
113 sprintf(buf, "Username already in use");
114 Log_debug("Username already in use");
115 sendServerReject(client, buf, MUMBLE_PROTO__REJECT__REJECT_TYPE__UsernameInUse);
119 if (strlen(getStrConf(PASSPHRASE)) > 0) {
120 if (!msg->payload.authenticate->password ||
121 (msg->payload.authenticate->password &&
122 strncmp(getStrConf(PASSPHRASE), msg->payload.authenticate->password, MAX_TEXT) != 0)) {
124 sprintf(buf, "Wrong server password");
125 sendServerReject(client, buf, MUMBLE_PROTO__REJECT__REJECT_TYPE__WrongServerPW);
126 Log_debug("Wrong server password: '%s'", msg->payload.authenticate->password != NULL ?
127 msg->payload.authenticate->password : "(null)");
131 if (strlen(msg->payload.authenticate->username) == 0 ||
132 strlen(msg->payload.authenticate->username) >= MAX_TEXT) { /* XXX - other invalid names? */
134 sprintf(buf, "Invalid username");
135 Log_debug("Invalid username");
136 sendServerReject(client, buf, MUMBLE_PROTO__REJECT__REJECT_TYPE__InvalidUsername);
140 if (Client_count() >= getIntConf(MAX_CLIENTS)) {
142 sprintf(buf, "Server is full (max %d users)", getIntConf(MAX_CLIENTS));
143 sendServerReject(client, buf, MUMBLE_PROTO__REJECT__REJECT_TYPE__ServerFull);
147 /* Name & password */
148 client->username = strdup(msg->payload.authenticate->username);
150 /* Setup UDP encryption */
151 CryptState_init(&client->cryptState);
152 CryptState_genKey(&client->cryptState);
153 sendmsg = Msg_create(CryptSetup);
154 sendmsg->payload.cryptSetup->has_key = true;
155 sendmsg->payload.cryptSetup->key.data = client->cryptState.raw_key;
156 sendmsg->payload.cryptSetup->key.len = AES_BLOCK_SIZE;
157 sendmsg->payload.cryptSetup->has_server_nonce = true;
158 sendmsg->payload.cryptSetup->server_nonce.data = client->cryptState.encrypt_iv;
159 sendmsg->payload.cryptSetup->server_nonce.len = AES_BLOCK_SIZE;
160 sendmsg->payload.cryptSetup->has_client_nonce = true;
161 sendmsg->payload.cryptSetup->client_nonce.data = client->cryptState.decrypt_iv;
162 sendmsg->payload.cryptSetup->client_nonce.len = AES_BLOCK_SIZE;
163 Client_send_message(client, sendmsg);
166 Chan_userJoin(defaultChan, client); /* Join default channel */
169 Log_debug("Client %d has %d CELT codecs", client->sessionId,
170 msg->payload.authenticate->n_celt_versions);
171 if (msg->payload.authenticate->n_celt_versions > 0) {
174 client->codec_count = msg->payload.authenticate->n_celt_versions;
176 for (i = 0; i < client->codec_count; i++)
177 Client_codec_add(client, msg->payload.authenticate->celt_versions[i]);
179 while (Client_codec_iterate(client, &codec_itr) != NULL)
180 Log_debug("Client %d CELT codec ver 0x%x", client->sessionId, codec_itr->codec);
183 Client_codec_add(client, (int32_t)0x8000000a);
184 client->codec_count = 1;
187 recheckCodecVersions();
189 sendmsg = Msg_create(CodecVersion);
190 sendmsg->payload.codecVersion->alpha = iCodecAlpha;
191 sendmsg->payload.codecVersion->beta = iCodecBeta;
192 sendmsg->payload.codecVersion->prefer_alpha = bPreferAlpha;
193 Client_send_message(client, sendmsg);
195 /* Iterate channels and send channel info */
197 while (Chan_iterate(&ch_itr) != NULL) {
198 sendmsg = Msg_create(ChannelState);
199 sendmsg->payload.channelState->has_channel_id = true;
200 sendmsg->payload.channelState->channel_id = ch_itr->id;
201 if (ch_itr->id != 0) {
202 sendmsg->payload.channelState->has_parent = true;
203 sendmsg->payload.channelState->parent = ch_itr->parent->id;
205 sendmsg->payload.channelState->name = strdup(ch_itr->name);
207 sendmsg->payload.channelState->description = strdup(ch_itr->desc);
208 Log_debug("Send channel info: %s", sendmsg->payload.channelState->name);
209 Client_send_message(client, sendmsg);
212 /* Iterate channels and send channel links info */
214 while (Chan_iterate(&ch_itr) != NULL) {
215 if (ch_itr->linkcount > 0) { /* Has links */
220 sendmsg = Msg_create(ChannelState);
221 sendmsg->payload.channelState->has_channel_id = true;
222 sendmsg->payload.channelState->channel_id = ch_itr->id;
223 sendmsg->payload.channelState->n_links = ch_itr->linkcount;
225 links = (uint32_t *)malloc(ch_itr->linkcount * sizeof(uint32_t));
226 list_iterate(itr, &ch_itr->channel_links) { /* Iterate links */
228 ch = list_get_entry(itr, channel_t, link_node);
231 sendmsg->payload.channelState->links = links;
232 Client_send_message(client, sendmsg);
236 /* Send user state for connecting user to other users */
237 sendmsg = Msg_create(UserState);
238 sendmsg->payload.userState->has_session = true;
239 sendmsg->payload.userState->session = client->sessionId;
240 sendmsg->payload.userState->name = strdup(client->username);
241 sendmsg->payload.userState->has_channel_id = true;
242 sendmsg->payload.userState->channel_id = ((channel_t *)client->channel)->id;
244 Client_send_message_except(client, sendmsg);
247 while (Client_iterate(&client_itr) != NULL) {
248 if (!IS_AUTH(client_itr))
250 sendmsg = Msg_create(UserState);
251 sendmsg->payload.userState->has_session = true;
252 sendmsg->payload.userState->session = client_itr->sessionId;
253 sendmsg->payload.userState->name = strdup(client_itr->username);
254 sendmsg->payload.userState->has_channel_id = true;
255 sendmsg->payload.userState->channel_id = ((channel_t *)client_itr->channel)->id;
257 /* Only self_mute/deaf supported */
258 if (client_itr->deaf) {
259 sendmsg->payload.userState->has_self_deaf = true;
260 sendmsg->payload.userState->self_deaf = true;
262 if (client_itr->mute) {
263 sendmsg->payload.userState->has_self_mute = true;
264 sendmsg->payload.userState->self_mute = true;
266 Client_send_message(client, sendmsg);
270 sendmsg = Msg_create(ServerSync);
271 sendmsg->payload.serverSync->has_session = true;
272 sendmsg->payload.serverSync->session = client->sessionId;
273 sendmsg->payload.serverSync->welcome_text = strdup(getStrConf(WELCOMETEXT));
274 sendmsg->payload.serverSync->has_max_bandwidth = true;
275 sendmsg->payload.serverSync->max_bandwidth = getIntConf(MAX_BANDWIDTH);
276 Client_send_message(client, sendmsg);
278 /* Server config message */
279 sendmsg = Msg_create(ServerConfig);
280 sendmsg->payload.serverConfig->has_allow_html = true;
281 sendmsg->payload.serverConfig->allow_html = true; /* Support this? */
282 sendmsg->payload.serverConfig->has_message_length = true;
283 sendmsg->payload.serverConfig->message_length = MAX_TEXT; /* Hardcoded */
284 sendmsg->payload.serverConfig->has_image_message_length = true;
285 sendmsg->payload.serverConfig->image_message_length = 0; /* XXX */
286 Client_send_message(client, sendmsg);
288 Log_info_client(client, "User %s authenticated", client->username);
292 if (msg->payload.ping->has_good)
293 client->cryptState.uiRemoteGood = msg->payload.ping->good;
294 if (msg->payload.ping->has_late)
295 client->cryptState.uiRemoteLate = msg->payload.ping->late;
296 if (msg->payload.ping->has_lost)
297 client->cryptState.uiRemoteLost = msg->payload.ping->lost;
298 if (msg->payload.ping->has_resync)
299 client->cryptState.uiRemoteResync = msg->payload.ping->resync;
301 Log_debug("Ping <-: %d %d %d %d",
302 client->cryptState.uiRemoteGood, client->cryptState.uiRemoteLate,
303 client->cryptState.uiRemoteLost, client->cryptState.uiRemoteResync
306 client->UDPPingAvg = msg->payload.ping->udp_ping_avg;
307 client->UDPPingVar = msg->payload.ping->udp_ping_var;
308 client->TCPPingAvg = msg->payload.ping->tcp_ping_avg;
309 client->TCPPingVar = msg->payload.ping->tcp_ping_var;
310 client->UDPPackets = msg->payload.ping->udp_packets;
311 client->TCPPackets = msg->payload.ping->tcp_packets;
313 sendmsg = Msg_create(Ping);
315 sendmsg->payload.ping->timestamp = msg->payload.ping->timestamp;
316 sendmsg->payload.ping->has_timestamp = true;
317 sendmsg->payload.ping->good = client->cryptState.uiGood;
318 sendmsg->payload.ping->has_good = true;
319 sendmsg->payload.ping->late = client->cryptState.uiLate;
320 sendmsg->payload.ping->has_late = true;
321 sendmsg->payload.ping->lost = client->cryptState.uiLost;
322 sendmsg->payload.ping->has_lost = true;
323 sendmsg->payload.ping->resync = client->cryptState.uiResync;
324 sendmsg->payload.ping->has_resync = true;
326 Client_send_message(client, sendmsg);
327 Log_debug("Ping ->: %d %d %d %d",
328 client->cryptState.uiGood, client->cryptState.uiLate,
329 client->cryptState.uiLost, client->cryptState.uiResync);
333 Log_debug("Voice channel crypt resync requested");
334 if (!msg->payload.cryptSetup->has_client_nonce) {
335 sendmsg = Msg_create(CryptSetup);
336 sendmsg->payload.cryptSetup->has_server_nonce = true;
337 sendmsg->payload.cryptSetup->server_nonce.data = client->cryptState.decrypt_iv;
338 sendmsg->payload.cryptSetup->server_nonce.len = AES_BLOCK_SIZE;
339 Client_send_message(client, sendmsg);
341 memcpy(client->cryptState.decrypt_iv, msg->payload.cryptSetup->client_nonce.data, AES_BLOCK_SIZE);
342 client->cryptState.uiResync++;
346 /* Only allow state changes for for the self user */
347 if (msg->payload.userState->has_session &&
348 msg->payload.userState->session != client->sessionId) {
349 sendPermissionDenied(client, "Permission denied");
352 if (msg->payload.userState->has_user_id || msg->payload.userState->has_mute ||
353 msg->payload.userState->has_deaf || msg->payload.userState->has_suppress ||
354 msg->payload.userState->has_texture) {
356 sendPermissionDenied(client, "Not supported by uMurmur");
360 msg->payload.userState->has_session = true;
361 msg->payload.userState->session = client->sessionId;
362 msg->payload.userState->has_actor = true;
363 msg->payload.userState->actor = client->sessionId;
365 if (msg->payload.userState->has_self_deaf) {
366 client->deaf = msg->payload.userState->self_deaf;
368 if (msg->payload.userState->has_self_mute) {
369 client->mute = msg->payload.userState->self_mute;
371 msg->payload.userState->has_self_deaf = true;
372 msg->payload.userState->self_deaf = false;
373 client->deaf = false;
376 if (msg->payload.userState->has_channel_id) {
378 if (!Chan_userJoin_id_test(msg->payload.userState->channel_id))
380 leave_id = Chan_userJoin_id(msg->payload.userState->channel_id, client);
382 Log_debug("Removing channel ID %d", leave_id);
383 sendmsg = Msg_create(ChannelRemove);
384 sendmsg->payload.channelRemove->channel_id = leave_id;
387 if (msg->payload.userState->has_plugin_context) {
389 free(client->context);
390 client->context = malloc(msg->payload.userState->plugin_context.len);
391 if (client->context == NULL)
392 Log_fatal("Out of memory");
393 memcpy(client->context, msg->payload.userState->plugin_context.data,
394 msg->payload.userState->plugin_context.len);
396 break; /* Don't inform other users about this state */
402 Client_send_message_except(NULL, msg);
404 /* Need to send remove channel message _after_ UserState message */
406 Client_send_message_except(NULL, sendmsg);
410 msg->payload.textMessage->has_actor = true;
411 msg->payload.textMessage->actor = client->sessionId;
413 /* XXX - HTML is allowed and can't be turned off */
414 if (msg->payload.textMessage->n_tree_id > 0) {
415 sendPermissionDenied(client, "Tree message not supported");
419 if (msg->payload.textMessage->n_channel_id > 0) { /* To channel */
422 for (i = 0; i < msg->payload.textMessage->n_channel_id; i++) {
425 Chan_iterate(&ch_itr);
426 } while (ch_itr != NULL && ch_itr->id != msg->payload.textMessage->channel_id[i]);
427 if (ch_itr != NULL) {
429 list_iterate(itr, &ch_itr->clients) {
431 c = list_get_entry(itr, client_t, chan_node);
432 if (c != client && !c->deaf) {
434 Client_send_message(c, msg);
435 Log_debug("Text message to session ID %d", c->sessionId);
441 if (msg->payload.textMessage->n_session > 0) { /* To user */
444 for (i = 0; i < msg->payload.textMessage->n_session; i++) {
446 while (Client_iterate(&itr) != NULL) {
449 if (itr->sessionId == msg->payload.textMessage->session[i]) {
452 Client_send_message(itr, msg);
453 Log_debug("Text message to session ID %d", itr->sessionId);
459 Log_warn("TextMessage: Session ID %d not found", msg->payload.textMessage->session[i]);
466 int i, j, count, targetId = msg->payload.voiceTarget->id;
467 struct _MumbleProto__VoiceTarget__Target *target;
469 if (!targetId || targetId >= 0x1f)
471 Voicetarget_add_id(client, targetId);
472 count = msg->payload.voiceTarget->n_targets;
475 for (i = 0; i < count; i++) {
476 target = msg->payload.voiceTarget->targets[i];
477 for (j = 0; j < target->n_session; j++)
478 Voicetarget_add_session(client, targetId, target->session[j]);
479 if (target->has_channel_id) {
480 bool_t linked = false, children = false;
481 if (target->has_links)
482 linked = target->links;
483 if (target->has_children)
484 children = target->children;
485 Voicetarget_add_channel(client, targetId, target->channel_id, linked, children);
491 Log_debug("Version message received");
492 if (msg->payload.version->has_version) {
493 client->version = msg->payload.version->version;
494 Log_debug("Client version 0x%x", client->version);
496 if (msg->payload.version->release) {
497 if (client->release) free(client->release);
498 client->release = strdup(msg->payload.version->release);
499 Log_debug("Client release %s", client->release);
501 if (msg->payload.version->os) {
502 if (client->os) free(client->os);
503 client->os = strdup(msg->payload.version->os);
504 Log_debug("Client OS %s", client->os);
506 if (msg->payload.version->os_version) {
507 if (client->os_version) free(client->os_version);
508 client->os_version = strdup(msg->payload.version->os_version);
509 Log_debug("Client OS version %s", client->os_version);
512 case PermissionQuery:
513 Msg_inc_ref(msg); /* Re-use message */
514 msg->payload.permissionQuery->has_permissions = true;
515 msg->payload.permissionQuery->permissions = PERM_DEFAULT;
517 Client_send_message(client, msg);
520 client->bUDP = false;
521 Client_voiceMsg(client, msg->payload.UDPTunnel->packet.data, msg->payload.UDPTunnel->packet.len);
525 channel_t *ch_itr, *parent, *newchan;
527 /* Don't allow any changes to existing channels */
528 if (msg->payload.channelState->has_channel_id) {
529 sendPermissionDenied(client, "Not supported by uMurmur");
532 /* Must have parent */
533 if (!msg->payload.channelState->has_parent) {
534 sendPermissionDenied(client, "Not supported by uMurmur");
538 if (msg->payload.channelState->name == NULL) {
539 sendPermissionDenied(client, "Not supported by uMurmur");
542 /* Must be temporary channel */
543 if (msg->payload.channelState->temporary != true) {
544 sendPermissionDenied(client, "Only temporary channels are supported by uMurmur");
547 /* Check channel name is OK */
548 if (strlen(msg->payload.channelState->name) > MAX_TEXT) {
549 sendPermissionDenied(client, "Channel name too long");
553 parent = Chan_fromId(msg->payload.channelState->parent);
557 while (Chan_iterate_siblings(parent, &ch_itr) != NULL) {
558 if (strcmp(ch_itr->name, msg->payload.channelState->name) == 0) {
559 sendPermissionDenied(client, "Channel already exists");
566 /* Disallow temporary channels as siblings to temporary channels */
567 if (parent->temporary) {
568 sendPermissionDenied(client, "Parent channel is temporary channel");
572 /* XXX - Murmur looks for "\\w" and sends perm denied if not found.
573 * I don't know why so I don't do that here...
576 /* Create the channel */
577 newchan = Chan_createChannel(msg->payload.channelState->name,
578 msg->payload.channelState->description);
579 newchan->temporary = true;
580 Chan_addChannel(parent, newchan);
581 msg->payload.channelState->has_channel_id = true;
582 msg->payload.channelState->channel_id = newchan->id;
584 Client_send_message_except(NULL, msg);
586 /* Join the creating user */
587 sendmsg = Msg_create(UserState);
588 sendmsg->payload.userState->has_session = true;
589 sendmsg->payload.userState->session = client->sessionId;
590 sendmsg->payload.userState->has_channel_id = true;
591 sendmsg->payload.userState->channel_id = newchan->id;
592 Client_send_message_except(NULL, sendmsg);
594 leave_id = Chan_userJoin(newchan, client);
596 Log_debug("Removing channel ID %d", leave_id);
597 sendmsg = Msg_create(ChannelRemove);
598 sendmsg->payload.channelRemove->channel_id = leave_id;
599 Client_send_message_except(NULL, sendmsg);
606 client_t *target = NULL;
607 codec_t *codec_itr = NULL;
609 bool_t details = true;
611 if (msg->payload.userStats->has_stats_only)
612 details = !msg->payload.userStats->stats_only;
614 if (!msg->payload.userStats->has_session)
615 sendPermissionDenied(client, "Not supported by uMurmur");
616 while (Client_iterate(&target) != NULL) {
617 if (!IS_AUTH(target))
619 if (target->sessionId == msg->payload.userStats->session)
622 if (!target) /* Not found */
626 * Differences from Murmur:
627 * o Ignoring certificates intentionally
628 * o Ignoring channel local determining
631 sendmsg = Msg_create(UserStats);
632 sendmsg->payload.userStats->session = msg->payload.userStats->session;
633 sendmsg->payload.userStats->from_client->has_good = true;
634 sendmsg->payload.userStats->from_client->good = target->cryptState.uiGood;
635 sendmsg->payload.userStats->from_client->has_late = true;
636 sendmsg->payload.userStats->from_client->late = target->cryptState.uiLate;
637 sendmsg->payload.userStats->from_client->has_lost = true;
638 sendmsg->payload.userStats->from_client->lost = target->cryptState.uiLost;
639 sendmsg->payload.userStats->from_client->has_resync = true;
640 sendmsg->payload.userStats->from_client->resync = target->cryptState.uiResync;
642 sendmsg->payload.userStats->from_server->has_good = true;
643 sendmsg->payload.userStats->from_server->good = target->cryptState.uiRemoteGood;
644 sendmsg->payload.userStats->from_server->has_late = true;
645 sendmsg->payload.userStats->from_server->late = target->cryptState.uiRemoteLate;
646 sendmsg->payload.userStats->from_server->has_lost = true;
647 sendmsg->payload.userStats->from_server->lost = target->cryptState.uiRemoteLost;
648 sendmsg->payload.userStats->from_server->has_resync = true;
649 sendmsg->payload.userStats->from_server->resync = target->cryptState.uiRemoteResync;
651 sendmsg->payload.userStats->has_udp_packets = true;
652 sendmsg->payload.userStats->udp_packets = target->UDPPackets;
653 sendmsg->payload.userStats->has_udp_ping_avg = true;
654 sendmsg->payload.userStats->udp_ping_avg = target->UDPPingAvg;
655 sendmsg->payload.userStats->has_udp_ping_var = true;
656 sendmsg->payload.userStats->udp_ping_var = target->UDPPingVar;
658 sendmsg->payload.userStats->has_tcp_ping_avg = true;
659 sendmsg->payload.userStats->tcp_ping_avg = target->TCPPingAvg;
660 sendmsg->payload.userStats->has_tcp_ping_var = true;
661 sendmsg->payload.userStats->tcp_ping_var = target->TCPPingVar;
662 sendmsg->payload.userStats->has_tcp_packets = true;
663 sendmsg->payload.userStats->tcp_packets = target->TCPPackets;
667 sendmsg->payload.userStats->version->has_version = true;
668 sendmsg->payload.userStats->version->version = target->version;
669 sendmsg->payload.userStats->version->release = strdup(target->release);
670 sendmsg->payload.userStats->version->os = strdup(target->os);
671 sendmsg->payload.userStats->version->os_version = strdup(target->os_version);
673 sendmsg->payload.userStats->n_celt_versions = target->codec_count;
674 sendmsg->payload.userStats->celt_versions = malloc(sizeof(int32_t) * target->codec_count);
675 if (!sendmsg->payload.userStats->celt_versions)
676 Log_fatal("Out of memory");
678 while (Client_codec_iterate(target, &codec_itr) != NULL)
679 sendmsg->payload.userStats->celt_versions[i++] = codec_itr->codec;
682 sendmsg->payload.userStats->has_address = true;
683 sendmsg->payload.userStats->address.data = malloc(sizeof(uint8_t) * 16);
684 if (!sendmsg->payload.userStats->address.data)
685 Log_fatal("Out of memory");
686 memset(sendmsg->payload.userStats->address.data, 0, 16);
687 /* ipv4 representation as ipv6 address. Supposedly correct. */
688 memcpy(&sendmsg->payload.userStats->address.data[12], &target->remote_tcp.sin_addr, 4);
689 sendmsg->payload.userStats->address.len = 16;
692 sendmsg->payload.userStats->has_bandwidth = true;
693 sendmsg->payload.userStats->bandwidth = target->availableBandwidth;
696 sendmsg->payload.userStats->has_onlinesecs = true;
697 sendmsg->payload.userStats->onlinesecs = Timer_elapsed(&target->connectTime) / 1000000LL;
699 sendmsg->payload.userStats->has_idlesecs = true;
700 sendmsg->payload.userStats->idlesecs = Timer_elapsed(&target->idleTime) / 1000000LL;
701 Client_send_message(client, sendmsg);
704 /* Permission denied for all these messages. Not implemented. */
707 case ContextActionAdd:
712 sendPermissionDenied(client, "Not supported by uMurmur");
716 Log_warn("Message %d not handled", msg->messageType);
725 Client_close(client);