void Server::msgAuthenticate(ServerUser *uSource, MumbleProto::Authenticate &msg) {
	if ((msg.tokens_size() > 0) || (uSource->sState == ServerUser::Authenticated)) {
		QStringList qsl;
		for (int i=0;i<msg.tokens_size();++i)
			qsl << u8(msg.tokens(i));
		{
			QMutexLocker qml(&qmCache);
			uSource->qslAccessTokens = qsl;
		}
		clearACLCache(uSource);
	}
	MSG_SETUP(ServerUser::Connected);

	Channel *root = qhChannels.value(0);
	Channel *c;

	uSource->qsName = u8(msg.username());

	bool ok = false;
	bool nameok = validateUserName(uSource->qsName);
	QString pw = u8(msg.password());

	// Fetch ID and stored username.
	// Since this may call DBus, which may recall our dbus messages, this function needs
	// to support re-entrancy, and also to support the fact that sessions may go away.
	int id = authenticate(uSource->qsName, pw, uSource->qslEmail, uSource->qsHash, uSource->bVerified, uSource->peerCertificateChain());

	uSource->iId = id >= 0 ? id : -1;

	QString reason;
	MumbleProto::Reject_RejectType rtType = MumbleProto::Reject_RejectType_None;

	if (! nameok && (uSource->iId == -1)) {
		reason = "Invalid username";
		rtType = MumbleProto::Reject_RejectType_InvalidUsername;
	} else if (id==-1) {
		reason = "Wrong password for user";
		rtType = MumbleProto::Reject_RejectType_WrongUserPW;
	} else if (id==-2 && ! qsPassword.isEmpty() && qsPassword != pw) {
		reason = "Invalid server password";
		rtType = MumbleProto::Reject_RejectType_WrongServerPW;
	} else {
		ok = true;
	}

	ServerUser *uOld = NULL;
	foreach(ServerUser *u, qhUsers) {
		if (u == uSource)
			continue;
		if (((u->iId>=0) && (u->iId == uSource->iId)) ||
		        (u->qsName == uSource->qsName)) {
			uOld = u;
			break;
		}
	}

	// Allow reuse of name from same IP
	if (ok && uOld && (uSource->iId == -1)) {
		if ((uOld->peerAddress() != uSource->peerAddress()) && (uSource->qsHash.isEmpty() || (uSource->qsHash != uOld->qsHash))) {
			reason = "Username already in use";
			rtType = MumbleProto::Reject_RejectType_UsernameInUse;
			ok = false;
		}
	}

	if ((id != 0) && (qhUsers.count() > iMaxUsers)) {
		reason = QString::fromLatin1("Server is full (max %1 users)").arg(iMaxUsers);
		rtType = MumbleProto::Reject_RejectType_ServerFull;
		ok = false;
	}

	if ((id != 0) && (uSource->qsHash.isEmpty() && bCertRequired)) {
		reason = QString::fromLatin1("A certificate is required to connect to this server");
		rtType = MumbleProto::Reject_RejectType_NoCertificate;
		ok = false;
	}

	if (! ok) {
		log(uSource, QString("Rejected connection: %1").arg(reason));
		MumbleProto::Reject mpr;
		mpr.set_reason(u8(reason));
		mpr.set_type(rtType);
		sendMessage(uSource, mpr);
		uSource->disconnectSocket();
		return;
	}

	startThread();

	// Kick ghost
	if (uOld) {
		log(uSource, "Disconnecting ghost");
		uOld->disconnectSocket(true);
	}

	// Setup UDP encryption
	uSource->csCrypt.genKey();

	MumbleProto::CryptSetup mpcrypt;
	mpcrypt.set_key(std::string(reinterpret_cast<const char *>(uSource->csCrypt.raw_key), AES_BLOCK_SIZE));
	mpcrypt.set_server_nonce(std::string(reinterpret_cast<const char *>(uSource->csCrypt.encrypt_iv), AES_BLOCK_SIZE));
	mpcrypt.set_client_nonce(std::string(reinterpret_cast<const char *>(uSource->csCrypt.decrypt_iv), AES_BLOCK_SIZE));
	sendMessage(uSource, mpcrypt);

	if (msg.celt_versions_size() > 0) {
		for (int i=0;i < msg.celt_versions_size(); ++i)
			uSource->qlCodecs.append(msg.celt_versions(i));
	} else {
		uSource->qlCodecs.append(static_cast<qint32>(0x8000000a));
	}
	recheckCodecVersions();

	MumbleProto::CodecVersion mpcv;
	mpcv.set_alpha(iCodecAlpha);
	mpcv.set_beta(iCodecBeta);
	mpcv.set_prefer_alpha(bPreferAlpha);
	sendMessage(uSource, mpcv);

	// Transmit channel tree
	QQueue<Channel *> q;
	QSet<Channel *> chans;
	q << root;
	MumbleProto::ChannelState mpcs;
	while (! q.isEmpty()) {
		c = q.dequeue();
		chans.insert(c);

		mpcs.Clear();

		mpcs.set_channel_id(c->iId);
		if (c->cParent)
			mpcs.set_parent(c->cParent->iId);
		if (c->iId == 0)
			mpcs.set_name(u8(qsRegName.isEmpty() ? QLatin1String("Root") : qsRegName));
		else
			mpcs.set_name(u8(c->qsName));

		mpcs.set_position(c->iPosition);

		if ((uSource->uiVersion >= 0x010202) && ! c->qbaDescHash.isEmpty())
			mpcs.set_description_hash(blob(c->qbaDescHash));
		else if (! c->qsDesc.isEmpty())
			mpcs.set_description(u8(c->qsDesc));

		sendMessage(uSource, mpcs);

		foreach(c, c->qlChannels)
			q.enqueue(c);
	}

	// Transmit links
	foreach(c, chans) {
		if (c->qhLinks.count() > 0) {
			mpcs.Clear();
			mpcs.set_channel_id(c->iId);

			foreach(Channel *l, c->qhLinks.keys())
				mpcs.add_links(l->iId);
			sendMessage(uSource, mpcs);
		}
	}

	// Transmit user profile
	MumbleProto::UserState mpus;

	Channel *lc = qhChannels.value(readLastChannel(uSource->iId));
	if (! lc || ! hasPermission(uSource, lc, ChanACL::Enter)) {
		lc = qhChannels.value(iDefaultChan);
		if (! lc || ! hasPermission(uSource, lc, ChanACL::Enter)) {
			lc = root;
		}
	}

	userEnterChannel(uSource, lc, mpus);

	uSource->sState = ServerUser::Authenticated;
	mpus.set_session(uSource->uiSession);
	mpus.set_name(u8(uSource->qsName));
	if (uSource->iId >= 0) {
		mpus.set_user_id(uSource->iId);

		hashAssign(uSource->qbaTexture, uSource->qbaTextureHash, getUserTexture(uSource->iId));

		if (! uSource->qbaTextureHash.isEmpty())
			mpus.set_texture_hash(blob(uSource->qbaTextureHash));
		else if (! uSource->qbaTexture.isEmpty())
			mpus.set_texture(blob(uSource->qbaTexture));

		const QMap<int, QString> &info = getRegistration(uSource->iId);
		if (info.contains(ServerDB::User_Comment)) {
			hashAssign(uSource->qsComment, uSource->qbaCommentHash, info.value(ServerDB::User_Comment));
			if (! uSource->qbaCommentHash.isEmpty())
				mpus.set_comment_hash(blob(uSource->qbaCommentHash));
			else if (! uSource->qsComment.isEmpty())
				mpus.set_comment(u8(uSource->qsComment));
		}
	}
	if (! uSource->qsHash.isEmpty())
		mpus.set_hash(u8(uSource->qsHash));
	if (uSource->cChannel->iId != 0)
		mpus.set_channel_id(uSource->cChannel->iId);

	sendAll(mpus, 0x010202);

	if ((uSource->qbaTexture.length() >= 4) && (qFromBigEndian<unsigned int>(reinterpret_cast<const unsigned char *>(uSource->qbaTexture.constData())) == 600 * 60 * 4))
		mpus.set_texture(blob(uSource->qbaTexture));
	if (! uSource->qsComment.isEmpty())
		mpus.set_comment(u8(uSource->qsComment));
	sendAll(mpus, ~ 0x010202);

	// Transmit other users profiles
	foreach(ServerUser *u, qhUsers) {
		if (u->sState != ServerUser::Authenticated)
			continue;

		if (u == uSource)
			continue;

		mpus.Clear();
		mpus.set_session(u->uiSession);
		mpus.set_name(u8(u->qsName));
		if (u->iId >= 0)
			mpus.set_user_id(u->iId);
		if (uSource->uiVersion >= 0x010202) {
			if (! u->qbaTextureHash.isEmpty())
				mpus.set_texture_hash(blob(u->qbaTextureHash));
			else if (! u->qbaTexture.isEmpty())
				mpus.set_texture(blob(u->qbaTexture));
		} else if ((uSource->qbaTexture.length() >= 4) && (qFromBigEndian<unsigned int>(reinterpret_cast<const unsigned char *>(uSource->qbaTexture.constData())) == 600 * 60 * 4)) {
			mpus.set_texture(blob(u->qbaTexture));
		}
		if (u->cChannel->iId != 0)
			mpus.set_channel_id(u->cChannel->iId);
		if (u->bDeaf)
			mpus.set_deaf(true);
		else if (u->bMute)
			mpus.set_mute(true);
		if (u->bSuppress)
			mpus.set_suppress(true);
		if (u->bPrioritySpeaker)
			mpus.set_priority_speaker(true);
		if (u->bSelfDeaf)
			mpus.set_self_deaf(true);
		else if (u->bSelfMute)
			mpus.set_self_mute(true);
		if ((uSource->uiVersion >= 0x010202) && ! u->qbaCommentHash.isEmpty())
			mpus.set_comment_hash(blob(u->qbaCommentHash));
		else if (! u->qsComment.isEmpty())
			mpus.set_comment(u8(u->qsComment));
		if (! u->qsHash.isEmpty())
			mpus.set_hash(u8(u->qsHash));

		sendMessage(uSource, mpus);
	}

	// Send syncronisation packet
	MumbleProto::ServerSync mpss;
	mpss.set_session(uSource->uiSession);
	if (! qsWelcomeText.isEmpty())
		mpss.set_welcome_text(u8(qsWelcomeText));
	mpss.set_max_bandwidth(iMaxBandwidth);

	if (uSource->iId == 0) {
		mpss.set_permissions(ChanACL::All);
	} else {
		QMutexLocker qml(&qmCache);
		ChanACL::hasPermission(uSource, root, ChanACL::Enter, acCache);
		mpss.set_permissions(acCache.value(uSource)->value(root));
	}

	sendMessage(uSource, mpss);

	MumbleProto::ServerConfig mpsc;
	mpsc.set_allow_html(bAllowHTML);
	mpsc.set_message_length(iMaxTextMessageLength);
	mpsc.set_image_message_length(iMaxImageMessageLength);
	sendMessage(uSource, mpsc);

	log(uSource, "Authenticated");

	emit userConnected(uSource);
}