Esempio n. 1
0
        /// <inheritdoc />
                #pragma warning disable CA1502 // TODO: Decomplexify
        public async Task HandleInterop(CommCommand command, CancellationToken cancellationToken)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            var query = command.Parameters;

            object content;
            Action postRespond          = null;
            ushort?overrideResponsePort = null;

            if (query.TryGetValue(Constants.DMParameterCommand, out var method))
            {
                content = new object();
                switch (method)
                {
                case Constants.DMCommandChat:
                    try
                    {
                        var message = JsonConvert.DeserializeObject <Response>(command.RawJson, new JsonSerializerSettings
                        {
                            ContractResolver = new CamelCasePropertyNamesContractResolver()
                        });
                        if (message.ChannelIds == null)
                        {
                            throw new InvalidOperationException("Missing ChannelIds field!");
                        }
                        if (message.Message == null)
                        {
                            throw new InvalidOperationException("Missing Message field!");
                        }
                        await chat.SendMessage(message.Message, message.ChannelIds, cancellationToken).ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        logger.LogDebug("Exception while decoding chat message! Exception: {0}", e);
                        goto default;
                    }

                    break;

                case Constants.DMCommandServerPrimed:
                    // currently unused, maybe in the future
                    break;

                case Constants.DMCommandEndProcess:
                    TerminationWasRequested = true;
                    process.Terminate();
                    return;

                case Constants.DMCommandNewPort:
                    lock (this)
                    {
                        if (!query.TryGetValue(Constants.DMParameterData, out var stringPortObject) || !UInt16.TryParse(stringPortObject as string, out var currentPort))
                        {
                            /////UHHHH
                            logger.LogWarning("DreamDaemon sent new port command without providing it's own!");
                            content = new ErrorMessage {
                                Message = "Missing stringified port as data parameter!"
                            };
                            break;
                        }

                        if (!nextPort.HasValue)
                        {
                            reattachInformation.Port = currentPort;                                     // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to
                        }
                        else
                        {
                            // nextPort is ready, tell DD to switch to that
                            // if it fails it'll kill itself
                            content = new Dictionary <string, ushort> {
                                { Constants.DMParameterData, nextPort.Value }
                            };
                            reattachInformation.Port = nextPort.Value;
                            overrideResponsePort     = currentPort;
                            nextPort = null;

                            // we'll also get here from SetPort so complete that task
                            var tmpTcs = portAssignmentTcs;
                            portAssignmentTcs = null;
                            if (tmpTcs != null)
                            {
                                postRespond = () => tmpTcs.SetResult(true);
                            }
                        }

                        portClosedForReboot = false;
                    }

                    break;

                case Constants.DMCommandApiValidate:
                    if (!launchSecurityLevel.HasValue)
                    {
                        logger.LogWarning("DreamDaemon requested API validation but no intial security level was passed to the session controller!");
                        apiValidationStatus = ApiValidationStatus.UnaskedValidationRequest;
                        content             = new ErrorMessage {
                            Message = "Invalid API validation request!"
                        };
                        break;
                    }

                    if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevelObject) || !Enum.TryParse <DreamDaemonSecurity>(stringMinimumSecurityLevelObject as string, out var minimumSecurityLevel))
                    {
                        apiValidationStatus = ApiValidationStatus.BadValidationRequest;
                    }
                    else
                    {
                        switch (minimumSecurityLevel)
                        {
                        case DreamDaemonSecurity.Safe:
                            apiValidationStatus = ApiValidationStatus.RequiresSafe;
                            break;

                        case DreamDaemonSecurity.Ultrasafe:
                            apiValidationStatus = ApiValidationStatus.RequiresUltrasafe;
                            break;

                        case DreamDaemonSecurity.Trusted:
                            apiValidationStatus = ApiValidationStatus.RequiresTrusted;
                            break;

                        default:
                            throw new InvalidOperationException("Enum.TryParse failed to validate the DreamDaemonSecurity range!");
                        }
                    }

                    break;

                case Constants.DMCommandWorldReboot:
                    if (ClosePortOnReboot)
                    {
                        chatJsonTrackingContext.Active = false;
                        content = new Dictionary <string, int> {
                            { Constants.DMParameterData, 0 }
                        };
                        portClosedForReboot = true;
                    }

                    var oldTcs = rebootTcs;
                    rebootTcs   = new TaskCompletionSource <object>();
                    postRespond = () => oldTcs.SetResult(null);
                    break;

                default:
                    content = new ErrorMessage {
                        Message = "Requested command not supported!"
                    };
                    break;
                }
            }
            else
            {
                content = new ErrorMessage {
                    Message = "Missing command parameter!"
                }
            };

            var json     = JsonConvert.SerializeObject(content);
            var response = await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicInteropResponse), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(json)), overrideResponsePort, cancellationToken).ConfigureAwait(false);

            if (response != Constants.DMResponseSuccess)
            {
                logger.LogWarning("Received error response while responding to interop: {0}", response);
            }

            postRespond?.Invoke();
        }

                #pragma warning restore CA1502

        /// <summary>
        /// Throws an <see cref="ObjectDisposedException"/> if <see cref="Dispose(bool)"/> has been called
        /// </summary>
        void CheckDisposed()
        {
            if (disposed)
            {
                throw new ObjectDisposedException(nameof(SessionController));
            }
        }
        /// <inheritdoc />
        public async Task HandleInterop(CommCommand command, CancellationToken cancellationToken)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            var query = command.Parameters;

            object content;
            Action postRespond = null;

            if (query.TryGetValue(Constants.DMParameterCommand, out var method))
            {
                content = new object();
                switch (method)
                {
                case Constants.DMCommandServerPrimed:
                    //currently unused, maybe in the future
                    break;

                case Constants.DMCommandEndProcess:
                    TerminationWasRequested = true;
                    process.Terminate();
                    return;

                case Constants.DMCommandNewPort:
                    lock (this)
                    {
                        if (!query.TryGetValue(Constants.DMParameterData, out var stringPort) || !UInt16.TryParse(stringPort, out var currentPort))
                        {
                            /////UHHHH
                            logger.LogWarning("DreamDaemon sent new port command without providing it's own!");
                            break;
                        }

                        if (!nextPort.HasValue)
                        {
                            //not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to
                            reattachInformation.Port = currentPort;
                        }
                        else
                        {
                            //nextPort is ready, tell DD to switch to that
                            //if it fails it'll kill itself
                            content = new Dictionary <string, ushort> {
                                { Constants.DMParameterData, nextPort.Value }
                            };
                            reattachInformation.Port = nextPort.Value;
                            nextPort = null;

                            //we'll also get here from SetPort so complete that task
                            var tmpTcs = portAssignmentTcs;
                            portAssignmentTcs = null;
                            if (tmpTcs != null)
                            {
                                postRespond = () => tmpTcs.SetResult(true);
                            }
                        }

                        portClosedForReboot = false;
                    }
                    break;

                case Constants.DMCommandApiValidate:
                    apiValidated = true;
                    break;

                case Constants.DMCommandWorldReboot:
                    if (ClosePortOnReboot)
                    {
                        content = new Dictionary <string, int> {
                            { Constants.DMParameterData, 0 }
                        };
                        portClosedForReboot = true;
                    }
                    var oldTcs = rebootTcs;
                    rebootTcs   = new TaskCompletionSource <object>();
                    postRespond = () => oldTcs.SetResult(null);
                    break;

                default:
                    content = new ErrorMessage {
                        Message = "Requested command not supported!"
                    };
                    break;
                }
            }
            else
            {
                content = new ErrorMessage {
                    Message = "Missing command parameter!"
                }
            };

            var json     = JsonConvert.SerializeObject(content);
            var response = await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicInteropResponse), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(json)), cancellationToken).ConfigureAwait(false);

            if (response != Constants.DMResponseSuccess)
            {
                logger.LogWarning("Recieved error response while responding to interop: {0}", response);
            }

            postRespond?.Invoke();
        }

        /// <summary>
        /// Throws an <see cref="ObjectDisposedException"/> if <see cref="Dispose(bool)"/> has been called
        /// </summary>
        void CheckDisposed()
        {
            if (disposed)
            {
                throw new ObjectDisposedException(nameof(SessionController));
            }
        }