/// <summary>
        /// Processes an incoming request already stored in the client input buffer.
        /// </summary>
        protected void ProcessRequest(ConnectedClient client, DataPacket request)
        {
            ResponsePacket response = null; // response to send

            try
            {
                // check whether the client is logged in
                if (!client.IsLoggedIn && FunctionID.RequiresLoggedIn(request.FunctionID))
                {
                    throw new ProtocolException(ErrorCode.AccessDenied, Locale.IsRussian ?
                                                "Требуется вход в систему." :
                                                "Login required.");
                }

                // process standard request
                bool handled = true; // request was handled

                switch (request.FunctionID)
                {
                case FunctionID.GetSessionInfo:
                    GetSessionInfo(client, request, out response);
                    break;

                case FunctionID.Login:
                    Login(client, request, out response);
                    break;

                case FunctionID.GetStatus:
                    GetStatus(client, request, out response);
                    break;

                case FunctionID.TerminateSession:
                    TerminateSession(client, request);
                    break;

                case FunctionID.GetFileInfo:
                    GetFileInfo(client, request, out response);
                    break;

                case FunctionID.DownloadFile:
                    DownloadFile(client, request);
                    break;

                case FunctionID.UploadFile:
                    UploadFile(client, request, out response);
                    break;

                default:
                    handled = false;
                    break;
                }

                // process custom request
                if (!handled)
                {
                    ProcessCustomRequest(client, request, out response, out handled);

                    if (!handled)
                    {
                        throw new ProtocolException(ErrorCode.IllegalFunction, Locale.IsRussian ?
                                                    "Недопустимая функция." :
                                                    "Illegal function.");
                    }
                }
            }
            catch (Exception ex)
            {
                log.WriteException(ex, Locale.IsRussian ?
                                   "Ошибка при обработке запроса 0x{0} для клиента {1}" :
                                   "Error processing request 0x{0} for the client {1}",
                                   request.FunctionID.ToString("X4"), client.Address);

                response = new ResponsePacket(request, client.OutBuf);
                response.SetError(ex is ProtocolException pe ? pe.ErrorCode : ErrorCode.InternalServerError);
            }

            // send response
            if (response != null)
            {
                client.SendResponse(response);
            }
        }
        /// <summary>
        /// Receives a data packet from the client.
        /// </summary>
        protected bool ReceiveDataPacket(ConnectedClient client, out DataPacket dataPacket)
        {
            bool   formatError = true;
            string errDescr    = "";

            byte[] buffer      = client.InBuf;
            int    bytesToRead = HeaderLength + 2;
            int    bytesRead   = client.NetStream.Read(buffer, 0, bytesToRead);

            dataPacket = null;

            if (bytesRead == bytesToRead)
            {
                DataPacket request = new DataPacket
                {
                    TransactionID = BitConverter.ToUInt16(buffer, 0),
                    DataLength    = BitConverter.ToInt32(buffer, 2),
                    SessionID     = BitConverter.ToInt64(buffer, 6),
                    FunctionID    = BitConverter.ToUInt16(buffer, 14),
                    Buffer        = buffer
                };

                if (request.DataLength + 6 > buffer.Length)
                {
                    errDescr = Locale.IsRussian ?
                               "длина данных слишком велика" :
                               "data length is too big";
                }
                else if (!(request.SessionID == 0 && request.FunctionID == FunctionID.GetSessionInfo ||
                           request.SessionID != 0 && request.SessionID == client.SessionID))
                {
                    errDescr = Locale.IsRussian ?
                               "неверный идентификатор сессии" :
                               "incorrect session ID";
                }
                else
                {
                    // read the rest of the data
                    bytesToRead = request.DataLength - 10;
                    bytesRead   = client.ReadData(HeaderLength + 2, bytesToRead);

                    if (bytesRead == bytesToRead)
                    {
                        formatError = false;
                        dataPacket  = request;
                    }
                    else
                    {
                        errDescr = Locale.IsRussian ?
                                   "не удалось прочитать все данные" :
                                   "unable to read all data";
                    }
                }
            }
            else
            {
                errDescr = Locale.IsRussian ?
                           "не удалось прочитать заголовок пакета данных" :
                           "unable to read data packet header";
            }

            if (formatError)
            {
                log.WriteError(Locale.IsRussian ?
                               "Некорректный формат данных, полученных от клиента {0}: {1}" :
                               "Incorrect format of data received from the client {0}: {1}",
                               client.Address, errDescr);

                ClearNetStream(client.NetStream, buffer);
            }

            return(dataPacket != null);
        }