/// <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)); } }