public Boolean authenticate(String username, String password)
        {
            _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: start");

            Message helloMessage = new Message();
            helloMessage.Method = "hello";
            helloMessage.putField("clientname", _clientName);
            helloMessage.putField("clientversion", _clientVersion);
            helloMessage.putField("htspversion", Message.HTSP_VERSION);
            helloMessage.putField("username", username);

            LoopBackHandler LoopBackHandler = new LoopBackHandler();
            sendMessage(helloMessage, LoopBackHandler);
            Message helloResponse = LoopBackHandler.getResponse();
            if (helloResponse != null)
            {
                if (helloResponse.containsField("htspversion"))
                {
                    _serverProtocolVersion = helloResponse.getInt("htspversion");
                }
                else
                {
                    _serverProtocolVersion = -1;
                    _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: hello don't deliver required field 'htspversion' - htsp wrong implemented on tvheadend side.");
                }

                if (helloResponse.containsField("servername"))
                {
                    _servername = helloResponse.getString("servername");
                }
                else
                {
                    _servername = "n/a";
                    _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: hello don't deliver required field 'servername' - htsp wrong implemented on tvheadend side.");
                }

                if (helloResponse.containsField("serverversion"))
                {
                    _serverversion = helloResponse.getString("serverversion");
                }
                else
                {
                    _serverversion = "n/a";
                    _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: hello don't deliver required field 'serverversion' - htsp wrong implemented on tvheadend side.");
                }

                byte[] salt = null;
                if (helloResponse.containsField("challenge"))
                {
                    salt = helloResponse.getByteArray("challenge");
                }
                else
                {
                    salt = new byte[0];
                    _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: hello don't deliver required field 'challenge' - htsp wrong implemented on tvheadend side.");
                }

                byte[] digest = Sha1.GenerateSaltedSHA1(password, salt);
                Message authMessage = new Message();
                authMessage.Method = "authenticate";
                authMessage.putField("username", username);
                authMessage.putField("digest", digest);
                sendMessage(authMessage, LoopBackHandler);
                Message authResponse = LoopBackHandler.getResponse();
                if (authResponse != null)
                {
                    Boolean auth = authResponse.getInt("noaccess", 0) != 1;
                    if (auth)
                    {
                        Message getDiskSpaceMessage = new Message();
                        getDiskSpaceMessage.Method = "getDiskSpace";
                        sendMessage(getDiskSpaceMessage, LoopBackHandler);
                        Message diskSpaceResponse = LoopBackHandler.getResponse();
                        if (diskSpaceResponse != null)
                        {
                            long freeDiskSpace = -1;
                            long totalDiskSpace = -1;
                            if (diskSpaceResponse.containsField("freediskspace"))
                            {
                                freeDiskSpace = diskSpaceResponse.getLong("freediskspace") / BytesPerGiga;
                            }
                            else
                            {
                                _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: getDiskSpace don't deliver required field 'freediskspace' - htsp wrong implemented on tvheadend side.");
                            }
                            if (diskSpaceResponse.containsField("totaldiskspace"))
                            {
                                totalDiskSpace = diskSpaceResponse.getLong("totaldiskspace") / BytesPerGiga;
                            }
                            else
                            {
                                _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: getDiskSpace don't deliver required field 'totaldiskspace' - htsp wrong implemented on tvheadend side.");
                            }

                            _diskSpace = freeDiskSpace + "GB / " + totalDiskSpace + "GB";
                        }

                        Message enableAsyncMetadataMessage = new Message();
                        enableAsyncMetadataMessage.Method = "enableAsyncMetadata";
                        sendMessage(enableAsyncMetadataMessage, null);
                    }

                    _logger.Info("[TVHclient] HTSConnectionAsync.authenticate: authenticated = " + auth);
                    return auth;
                }
            }
            _logger.Error("[TVHclient] HTSConnectionAsync.authenticate: no hello response");
            return false;
        }
        private static Message deserializeBinary(byte[] messageData)
        {
            byte type, namelen;
            long datalen;

            Message msg = new Message();
            int cnt = 0;

            ByteBuffer buf = new ByteBuffer(messageData);
            while (buf.hasRemaining())
            {
                type = buf.get();
                namelen = buf.get();
                datalen = uIntToLong(buf.get(), buf.get(), buf.get(), buf.get());

                if (buf.Length() < namelen + datalen)
                {
                    throw new IOException("Buffer limit exceeded");
                }

                //Get the key for the map (the name)
                string name = null;
                if (namelen == 0)
                {
                    name = Convert.ToString(cnt++);
                }
                else
                {
                    byte[] bName = new byte[namelen];
                    buf.get(bName);
                    name = NewString(bName);
                }

                //Get the actual content
                object obj = null;
                byte[] bData = new byte[datalen];
                buf.get(bData);

                switch (type)
                {
                    case HMF_STR:
                        {
                            obj = NewString(bData);
                            break;
                        }
                    case HMF_BIN:
                        {
                            obj = bData;
                            break;
                        }
                    case HMF_S64:
                        {
                            obj = toBigInteger(bData);
                            break;
                        }
                    case HMF_MAP:
                        {
                            obj = deserializeBinary(bData);
                            break;
                        }
                    case HMF_LIST:
                        {
                            obj = new List<object>(deserializeBinary(bData)._dict.Values);
                            break;
                        }
                    default:
                        throw new IOException("Unknown data type");
                }
                msg.putField(name, obj);
            }
            return msg;
        }
        public void sendMessage(Message message, IResponseHandler responseHandler)
        {
            // loop the sequence number
            if (_seq == int.MaxValue)
            {
                _seq = int.MinValue;
            }
            else
            {
                _seq++;
            }

            // housekeeping verry old response handlers
            if (_responseHandlers.ContainsKey(_seq))
            {
                _responseHandlers.Remove(_seq);
            }

            message.putField("seq", _seq);
            _messagesForSendQueue.Enqueue(message);
            _responseHandlers.Add(_seq, responseHandler);
        }