using System; using System.Collections.Generic; using System.IO; using System.ServiceProcess; using System.Threading.Tasks; namespace TGServerService { static class Program { /// /// Entry point to the program /// static void Main() { using (var S = new Service()) ServiceBase.Run(S); } /// /// Copy a file from to , but first ensure the destination directory exists /// /// The source file /// The destination file /// If , will overwrite if it is a file. Otherwise, if exists, an exception will be thrown public static void CopyFileForceDirectories(string source, string dest, bool overwrite) { try { Directory.CreateDirectory(Path.GetDirectoryName(dest)); } catch { } //we don't care if the above errors File.Copy(source, dest, overwrite); //if this throws errors thats all we care about } //http://stackoverflow.com/questions/1701457/directory-delete-doesnt-work-access-denied-error-but-under-windows-explorer-it /// /// Recursive directory deleter /// /// The directory to delete /// If , an empty will remain instead of being deleted fully. Incompatible with /// If any files or directories in the root level of match anything in this of s, they won't be deleted. Incompatible with public static async void DeleteDirectory(string path, bool ContentsOnly = false, IList excludeRoot = null) { var di = new DirectoryInfo(path); if (!di.Exists) return; if (excludeRoot != null) for (var I = 0; I < excludeRoot.Count; ++I) excludeRoot[I] = excludeRoot[I].ToLower(); if (CheckDeleteSymlinkDir(di)) return; await NormalizeAndDelete(di, excludeRoot, false); if (!ContentsOnly) { if (excludeRoot != null && excludeRoot.Count > 0) throw new Exception("Cannot fully delete folder with exclusions specified!"); di.Delete(true); } } /// /// Properly unlinks directory if it is a symlink /// /// for the directory in question /// if was a symlink and deleted, otherwise static bool CheckDeleteSymlinkDir(DirectoryInfo di) { if (!di.Attributes.HasFlag(FileAttributes.Directory)) { //this is probably a symlink Directory.Delete(di.FullName); return true; } return false; } /// /// Recursively empty a directory /// /// of the directory to empty /// Lowercase file and directory names to skip while emptying this level. Not passed forward /// If , will be deleted before the function exits static async Task NormalizeAndDelete(DirectoryInfo dir, IList excludeRoot, bool deleteRoot) { var tasks = new List { Task.Factory.StartNew(() => { foreach (var file in dir.GetFiles()) { if (excludeRoot != null && excludeRoot.Contains(file.Name.ToLower())) continue; file.Attributes = FileAttributes.Normal; file.Delete(); } }) }; foreach (var subDir in dir.GetDirectories()) { if (excludeRoot != null && excludeRoot.Contains(subDir.Name.ToLower())) continue; if (CheckDeleteSymlinkDir(subDir)) continue; tasks.Add(NormalizeAndDelete(subDir, null, true)); } await Task.WhenAll(tasks); if(deleteRoot) dir.Delete(true); } /// /// Recusively copy a directory /// /// The directory to copy /// The destination directory /// List of files and directories to ignore while copying /// If no error will be thrown if does not exist public static async void CopyDirectory(string sourceDirName, string destDirName, IList ignore = null, bool ignoreIfNotExists = false) { IList realIgnore; if (ignore != null) { realIgnore = new List(); foreach (var I in ignore) realIgnore.Add(I.ToLower()); } else realIgnore = null; await CopyDirectoryImpl(sourceDirName, destDirName, realIgnore, ignoreIfNotExists); } /// /// Recusively copy a directory /// /// The directory to copy /// The destination directory /// List of lowercase files and directories to ignore while copying /// If no error will be thrown if does not exist static async Task CopyDirectoryImpl(string sourceDirName, string destDirName, IList ignore, bool ignoreIfNotExists) { // If the destination directory doesn't exist, create it. if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); } // Get the subdirectories for the specified directory. DirectoryInfo dir = new DirectoryInfo(sourceDirName); if (!dir.Exists) { if (ignoreIfNotExists) return; throw new DirectoryNotFoundException( "Source directory does not exist or could not be found: " + sourceDirName); } DirectoryInfo[] dirs = dir.GetDirectories(); // Get the files in the directory and copy them to the new location. FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { if (ignore != null && ignore.Contains(file.Name.ToLower())) continue; string temppath = Path.Combine(destDirName, file.Name); file.CopyTo(temppath, true); } var tasks = new List(); // copy them and their contents to new location. foreach (DirectoryInfo subdir in dirs) { if (ignore != null && ignore.Contains(subdir.Name.ToLower())) continue; string temppath = Path.Combine(destDirName, subdir.Name); tasks.Add(CopyDirectoryImpl(subdir.FullName, temppath, ignore, false)); } await Task.WhenAll(tasks); } /// /// Properly escapes characters for a BYOND Topic() packet. See http://www.byond.com/docs/ref/info.html#/proc/list2params /// /// The to sanitize /// The sanitized string public static string SanitizeTopicString(string input) { return input.Replace("%", "%25").Replace("=", "%3d").Replace(";", "%3b").Replace("&", "%26").Replace("+", "%2b"); } /// /// Normalizes different versions of a path /// /// The path to normalize /// The normalized path public static string NormalizePath(string path) { return Path.GetFullPath(new Uri(path).LocalPath) .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) .ToUpperInvariant(); } } }