/// <summary> /// Runs when the <see cref="fileSystemWatcher"/> triggers /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The <see cref="FileSystemEventArgs"/></param> async void HandleWrite(object sender, FileSystemEventArgs e) // this is what async void was made for { try { var fileBytes = await ioManager.ReadAllBytes(e.FullPath, cancellationToken).ConfigureAwait(false); var file = Encoding.UTF8.GetString(fileBytes); logger.LogTrace("Read interop command json: {0}", file); CommCommand command; try { command = new CommCommand { Parameters = JsonConvert.DeserializeObject <IReadOnlyDictionary <string, object> >(file), RawJson = file }; } catch (JsonException ex) { // file not fully written yet logger.LogDebug("Suppressing json convert exception for command file write: {0}", ex); return; } await(handler?.HandleInterop(command, cancellationToken) ?? Task.CompletedTask).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (Exception ex) { logger.LogError("Exception while trying to handle command json write: {0}", ex); } }
/// <inheritdoc /> public async Task TrustDmbPath(string fullDmbPath, CancellationToken cancellationToken) { if (fullDmbPath == null) { throw new ArgumentNullException(nameof(fullDmbPath)); } using (await SemaphoreSlimContext.Lock(trustedFileSemaphore, cancellationToken).ConfigureAwait(false)) { string trustedFileText; if (await ioManager.FileExists(trustedFilePath, cancellationToken).ConfigureAwait(false)) { var trustedFileBytes = await ioManager.ReadAllBytes(trustedFilePath, cancellationToken).ConfigureAwait(false); trustedFileText = Encoding.UTF8.GetString(trustedFileBytes); trustedFileText = $"{trustedFileText.Trim()}{Environment.NewLine}"; } else { trustedFileText = String.Empty; } if (trustedFileText.Contains(fullDmbPath, StringComparison.Ordinal)) { return; } trustedFileText = $"{trustedFileText}{fullDmbPath}{Environment.NewLine}"; var newTrustedFileBytes = Encoding.UTF8.GetBytes(trustedFileText); await ioManager.WriteAllBytes(trustedFilePath, newTrustedFileBytes, cancellationToken).ConfigureAwait(false); } }
/// <inheritdoc /> public async Task <IReadOnlyList <CustomCommand> > GetCustomCommands(CancellationToken cancellationToken) { try { var resultBytes = await ioManager.ReadAllBytes(commandsPath, cancellationToken).ConfigureAwait(false); var resultJson = Encoding.UTF8.GetString(resultBytes); var result = JsonConvert.DeserializeObject <List <CustomCommand> >(resultJson, new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() } }); foreach (var I in result) { I.SetHandler(customCommandHandler); } return(result); } catch { return(new List <CustomCommand>()); } }
/// <summary> /// Adds server side includes to the .dme being compiled /// </summary> /// <param name="job">The <see cref="Models.CompileJob"/> for the operation</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param> /// <returns>A <see cref="Task"/> representing the running operation</returns> async Task ModifyDme(Models.CompileJob job, CancellationToken cancellationToken) { var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); var dmeFileName = String.Join('.', job.DmeName, DmeExtension); var dmePath = ioManager.ConcatPath(dirA, dmeFileName); var dmeReadTask = ioManager.ReadAllBytes(dmePath, cancellationToken); var dmeModificationsTask = configuration.CopyDMFilesTo(dmeFileName, ioManager.ResolvePath(dirA), cancellationToken); var dmeBytes = await dmeReadTask.ConfigureAwait(false); var dme = Encoding.UTF8.GetString(dmeBytes); var dmeModifications = await dmeModificationsTask.ConfigureAwait(false); if (dmeModifications == null || dmeModifications.TotalDmeOverwrite) { if (dmeModifications != null) { logger.LogDebug(".dme replacement configured!"); } else { logger.LogTrace("No .dme modifications required."); } return; } if (dmeModifications.HeadIncludeLine != null) { logger.LogDebug("Head .dme include line: {0}", dmeModifications.HeadIncludeLine); } if (dmeModifications.TailIncludeLine != null) { logger.LogDebug("Tail .dme include line: {0}", dmeModifications.TailIncludeLine); } var dmeLines = new List <string>(dme.Split(new[] { Environment.NewLine }, StringSplitOptions.None)); for (var I = 0; I < dmeLines.Count; ++I) { var line = dmeLines[I]; if (line.Contains("BEGIN_INCLUDE", StringComparison.Ordinal) && dmeModifications.HeadIncludeLine != null) { dmeLines.Insert(I + 1, dmeModifications.HeadIncludeLine); ++I; } else if (line.Contains("END_INCLUDE", StringComparison.Ordinal) && dmeModifications.TailIncludeLine != null) { dmeLines.Insert(I, dmeModifications.TailIncludeLine); break; } } dmeBytes = Encoding.UTF8.GetBytes(String.Join(Environment.NewLine, dmeLines)); await ioManager.WriteAllBytes(dmePath, dmeBytes, cancellationToken).ConfigureAwait(false); }
public async Task <IActionResult> GetLog(string path, CancellationToken cancellationToken) { if (path == null) { throw new ArgumentNullException(nameof(path)); } path = HttpUtility.UrlDecode(path); // guard against directory navigation var sanitizedPath = ioManager.GetFileName(path); if (path != sanitizedPath) { return(Forbid()); } var fullPath = ioManager.ConcatPath( fileLoggingConfiguration.GetFullLogDirectory(ioManager, assemblyInformationProvider, platformIdentifier), path); try { var fileTransferTicket = fileTransferService.CreateDownload( new FileDownloadProvider( () => null, null, fullPath, true)); var readTask = ioManager.ReadAllBytes(fullPath, cancellationToken); return(Ok(new LogFileResponse { Name = path, LastModified = await ioManager.GetLastModified(fullPath, cancellationToken).ConfigureAwait(false), FileTicket = fileTransferTicket.FileTicket, })); } catch (IOException ex) { return(Conflict(new ErrorMessageResponse(ErrorCode.IOError) { AdditionalData = ex.ToString(), })); } }
/// <inheritdoc /> public async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken) { async Task <IReadOnlyList <string> > GetIgnoreFiles() { var ignoreFileBytes = await ioManager.ReadAllBytes(StaticIgnorePath(), cancellationToken).ConfigureAwait(false); var ignoreFileText = Encoding.UTF8.GetString(ignoreFileBytes); var results = new List <string> { StaticIgnoreFile }; //we don't want to lose trailing whitespace on linux using (var reader = new StringReader(ignoreFileText)) { cancellationToken.ThrowIfCancellationRequested(); var line = await reader.ReadLineAsync().ConfigureAwait(false); if (!String.IsNullOrEmpty(line)) { results.Add(line); } } return(results); }; IReadOnlyList <string> ignoreFiles; async Task SymlinkBase(bool files) { Task <IReadOnlyList <string> > task; if (files) { task = ioManager.GetFiles(GameStaticFilesSubdirectory, cancellationToken); } else { task = ioManager.GetDirectories(GameStaticFilesSubdirectory, cancellationToken); } var entries = await task.ConfigureAwait(false); await Task.WhenAll(entries.Select(async x => { var fileName = ioManager.GetFileName(x); bool ignored; if (platformIdentifier.IsWindows) { //need to normalize ignored = ignoreFiles.Any(y => fileName.ToUpperInvariant() == y.ToUpperInvariant()); } else { ignored = ignoreFiles.Any(y => fileName == y); } if (ignored) { logger.LogTrace("Ignoring static file {0}...", fileName); return; } var destPath = ioManager.ConcatPath(destination, fileName); logger.LogTrace("Symlinking {0} to {1}...", x, destPath); var fileExistsTask = ioManager.FileExists(destPath, cancellationToken); if (await ioManager.DirectoryExists(destPath, cancellationToken).ConfigureAwait(false)) { await ioManager.DeleteDirectory(destPath, cancellationToken).ConfigureAwait(false); } var fileExists = await fileExistsTask.ConfigureAwait(false); if (fileExists) { await ioManager.DeleteFile(destPath, cancellationToken).ConfigureAwait(false); } await symlinkFactory.CreateSymbolicLink(ioManager.ResolvePath(x), ioManager.ResolvePath(destPath), cancellationToken).ConfigureAwait(false); })).ConfigureAwait(false); } using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { await EnsureDirectories(cancellationToken).ConfigureAwait(false); ignoreFiles = await GetIgnoreFiles().ConfigureAwait(false); await Task.WhenAll(SymlinkBase(true), SymlinkBase(false)).ConfigureAwait(false); } }
/// <inheritdoc /> public async Task <bool> CheckRunWizard(CancellationToken cancellationToken) { var setupWizardMode = generalConfiguration.SetupWizardMode; logger.LogTrace("Checking if setup wizard should run. SetupWizardMode: {0}", setupWizardMode); if (setupWizardMode == SetupWizardMode.Never) { logger.LogTrace("Skipping due to configuration..."); return(false); } var forceRun = setupWizardMode == SetupWizardMode.Force || setupWizardMode == SetupWizardMode.Only; if (!console.Available) { if (forceRun) { throw new InvalidOperationException("Asked to run setup wizard with no console avaliable!"); } logger.LogTrace("Skipping due to console not being available..."); return(false); } var userConfigFileName = String.Format(CultureInfo.InvariantCulture, "appsettings.{0}.json", hostingEnvironment.EnvironmentName); var exists = await ioManager.FileExists(userConfigFileName, cancellationToken).ConfigureAwait(false); bool shouldRunBasedOnAutodetect; if (exists) { var bytes = await ioManager.ReadAllBytes(userConfigFileName, cancellationToken).ConfigureAwait(false); var contents = Encoding.UTF8.GetString(bytes); var existingConfigIsEmpty = String.IsNullOrWhiteSpace(contents); logger.LogTrace("Configuration json detected. Empty: {0}", existingConfigIsEmpty); shouldRunBasedOnAutodetect = existingConfigIsEmpty; } else { shouldRunBasedOnAutodetect = true; logger.LogTrace("No configuration json detected"); } if (!shouldRunBasedOnAutodetect) { if (forceRun) { logger.LogTrace("Asking user to bypass due to force run request..."); await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "The configuration settings are requesting the setup wizard be run, but you already appear to have a configuration file ({0})!", userConfigFileName), true, cancellationToken).ConfigureAwait(false); forceRun = await PromptYesNo("Continue running setup wizard? (y/n): ", cancellationToken).ConfigureAwait(false); } if (!forceRun) { return(false); } } //flush the logs to prevent console conflicts await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); await RunWizard(userConfigFileName, cancellationToken).ConfigureAwait(false); return(true); }
/// <inheritdoc /> public async Task StartAsync(CancellationToken cancellationToken) { async Task <byte[]> GetActiveVersion() { var activeVersionFileExists = await ioManager.FileExists(ActiveVersionFileName, cancellationToken).ConfigureAwait(false); return(!activeVersionFileExists ? null : await ioManager.ReadAllBytes(ActiveVersionFileName, cancellationToken).ConfigureAwait(false)); } var activeVersionBytesTask = GetActiveVersion(); // Create local cfg directory in case it doesn't exist var localCfgDirectory = ioManager.ConcatPath( byondInstaller.PathToUserByondFolder, CfgDirectoryName); await ioManager.CreateDirectory( localCfgDirectory, cancellationToken).ConfigureAwait(false); // Delete trusted.txt so it doesn't grow too large var trustedFilePath = ioManager.ConcatPath( localCfgDirectory, TrustedDmbFileName); logger.LogTrace("Deleting trusted .dmbs file {0}", trustedFilePath); await ioManager.DeleteFile( trustedFilePath, cancellationToken).ConfigureAwait(false); var byondDirectory = ioManager.ResolvePath(); await ioManager.CreateDirectory(byondDirectory, cancellationToken).ConfigureAwait(false); var directories = await ioManager.GetDirectories(byondDirectory, cancellationToken).ConfigureAwait(false); async Task ReadVersion(string path) { var versionFile = ioManager.ConcatPath(path, VersionFileName); if (!await ioManager.FileExists(versionFile, cancellationToken).ConfigureAwait(false)) { logger.LogInformation("Cleaning unparsable version path: {0}", ioManager.ResolvePath(path)); await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); // cleanup return; } var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken).ConfigureAwait(false); var text = Encoding.UTF8.GetString(bytes); if (Version.TryParse(text, out var version)) { var key = VersionKey(version); lock (installedVersions) if (!installedVersions.ContainsKey(key)) { logger.LogDebug("Adding detected BYOND version {0}...", key); installedVersions.Add(key, Task.CompletedTask); return; } } await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); } await Task.WhenAll(directories.Select(x => ReadVersion(x))).ConfigureAwait(false); var activeVersionBytes = await activeVersionBytesTask.ConfigureAwait(false); if (activeVersionBytes != null) { var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes); bool hasRequestedActiveVersion; lock (installedVersions) hasRequestedActiveVersion = installedVersions.ContainsKey(activeVersionString); if (hasRequestedActiveVersion && Version.TryParse(activeVersionString, out var activeVersion)) { ActiveVersion = activeVersion.Semver(); } else { logger.LogWarning("Failed to load saved active version {0}!", activeVersionString); await ioManager.DeleteFile(ActiveVersionFileName, cancellationToken).ConfigureAwait(false); } } }
/// <summary> /// Check if it should and run the <see cref="SetupWizard"/> if necessary. /// </summary> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param> /// <returns>A <see cref="Task"/> representing the running operation.</returns> async Task CheckRunWizard(CancellationToken cancellationToken) { var setupWizardMode = generalConfiguration.SetupWizardMode; if (setupWizardMode == SetupWizardMode.Never) { return; } var forceRun = setupWizardMode == SetupWizardMode.Force || setupWizardMode == SetupWizardMode.Only; if (!console.Available) { if (forceRun) { throw new InvalidOperationException("Asked to run setup wizard with no console avaliable!"); } return; } var userConfigFileName = String.Format(CultureInfo.InvariantCulture, "appsettings.{0}.json", hostingEnvironment.EnvironmentName); async Task HandleSetupCancel() { // DCTx2: Operation should always run await console.WriteAsync(String.Empty, true, default).ConfigureAwait(false); await console.WriteAsync("Aborting setup!", true, default).ConfigureAwait(false); } // Link passed cancellationToken with cancel key press Task finalTask = Task.CompletedTask; using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, console.CancelKeyPress)) using ((cancellationToken = cts.Token).Register(() => finalTask = HandleSetupCancel())) try { var exists = await ioManager.FileExists(userConfigFileName, cancellationToken).ConfigureAwait(false); bool shouldRunBasedOnAutodetect; if (exists) { var bytes = await ioManager.ReadAllBytes(userConfigFileName, cancellationToken).ConfigureAwait(false); var contents = Encoding.UTF8.GetString(bytes); var existingConfigIsEmpty = String.IsNullOrWhiteSpace(contents) || contents.Trim() == "{}"; shouldRunBasedOnAutodetect = existingConfigIsEmpty; } else { shouldRunBasedOnAutodetect = true; } if (!shouldRunBasedOnAutodetect) { if (forceRun) { await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "The configuration settings are requesting the setup wizard be run, but you already appear to have a configuration file ({0})!", userConfigFileName), true, cancellationToken).ConfigureAwait(false); forceRun = await PromptYesNo("Continue running setup wizard? (y/n): ", cancellationToken).ConfigureAwait(false); } if (!forceRun) { return; } } // flush the logs to prevent console conflicts await asyncDelayer.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); await RunWizard(userConfigFileName, cancellationToken).ConfigureAwait(false); } finally { await finalTask.ConfigureAwait(false); } }
/// <inheritdoc /> public async Task StartAsync(CancellationToken cancellationToken) { async Task <byte[]> GetActiveVersion() { var activeVersionFileExists = await ioManager.FileExists(ActiveVersionFileName, cancellationToken).ConfigureAwait(false); return(!activeVersionFileExists ? null : await ioManager.ReadAllBytes(ActiveVersionFileName, cancellationToken).ConfigureAwait(false)); } var activeVersionBytesTask = GetActiveVersion(); await ioManager.CreateDirectory(".", cancellationToken).ConfigureAwait(false); var directories = await ioManager.GetDirectories(".", cancellationToken).ConfigureAwait(false); async Task ReadVersion(string path) { var versionFile = ioManager.ConcatPath(path, VersionFileName); if (!await ioManager.FileExists(versionFile, cancellationToken).ConfigureAwait(false)) { logger.LogInformation("Cleaning unparsable version path: {0}", ioManager.ResolvePath(path)); await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); // cleanup return; } var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken).ConfigureAwait(false); var text = Encoding.UTF8.GetString(bytes); if (Version.TryParse(text, out var version)) { var key = VersionKey(version); lock (installedVersions) if (!installedVersions.ContainsKey(key)) { installedVersions.Add(key, Task.CompletedTask); return; } } await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); } await Task.WhenAll(directories.Select(x => ReadVersion(x))).ConfigureAwait(false); var activeVersionBytes = await activeVersionBytesTask.ConfigureAwait(false); if (activeVersionBytes != null) { var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes); if (Version.TryParse(activeVersionString, out var activeVersion)) { ActiveVersion = activeVersion; } else { await ioManager.DeleteFile(ActiveVersionFileName, cancellationToken).ConfigureAwait(false); } } }