using System; using System.Configuration; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Web; using System.Web.Security; using System.Globalization; using ScrewTurn.Wiki.PluginFramework; using System.Reflection; using System.Net; namespace ScrewTurn.Wiki { /// /// Contains useful Tools. /// public static class Tools { /// /// Gets all the included files for the HTML Head, such as CSS, JavaScript and Icon pluginAssemblies, for a namespace. /// /// The namespace (null for the root). /// The includes. public static string GetIncludes(string nspace) { string theme = Settings.GetTheme(nspace); string themePath = Settings.GetThemePath(nspace); StringBuilder result = new StringBuilder(300); string[] css = Directory.GetFiles(Settings.ThemesDirectory + theme, "*.css"); string firstChunk; for(int i = 0; i < css.Length; i++) { if(Path.GetFileName(css[i]).IndexOf("_") != -1) { firstChunk = Path.GetFileName(css[i]).Substring(0, Path.GetFileName(css[i]).IndexOf("_")).ToLower(CultureInfo.CurrentCulture); if(firstChunk.Equals("screen") || firstChunk.Equals("print") || firstChunk.Equals("all") || firstChunk.Equals("aural") || firstChunk.Equals("braille") || firstChunk.Equals("embossed") || firstChunk.Equals("handheld") || firstChunk.Equals("projection") || firstChunk.Equals("tty") || firstChunk.Equals("tv")) { result.Append(@"" + "\n"); } else { result.Append(@"" + "\n"); } } else { result.Append(@"" + "\n"); } } string customEditorCss = Path.Combine(Settings.ThemesDirectory, theme); customEditorCss = Path.Combine(customEditorCss, "Editor.css"); if(File.Exists(customEditorCss)) result.AppendFormat(@"" + "\n", theme); else result.Append(@"" + "\n"); // OpenSearch result.AppendFormat(@"", Settings.MainUrl, Settings.WikiTitle + " - Search"); string[] js = Directory.GetFiles(Settings.ThemesDirectory + theme, "*.js"); for(int i = 0; i < js.Length; i++) { result.Append(@"" + "\n"); } string[] icons = Directory.GetFiles(Settings.ThemesDirectory + theme, "Icon.*"); if(icons.Length > 0) { result.Append(@"" + "\n"); } result.Append(GetJavaScriptIncludes()); // Include HTML Head result.Append(Settings.Provider.GetMetaDataItem(MetaDataItem.HtmlHead, null)); return result.ToString(); } /// /// Gets all the JavaScript files to include. /// /// The JS files. public static string GetJavaScriptIncludes() { StringBuilder buffer = new StringBuilder(100); foreach(string js in Directory.GetFiles(Settings.JsDirectory, "*.js")) { buffer.Append(@"" + "\n"); } return buffer.ToString(); } /// /// Converts a byte number into a string, formatted using KB, MB or GB. /// /// The # of bytes. /// The formatted string. public static string BytesToString(long bytes) { if(bytes < 1024) return bytes.ToString() + " B"; else if(bytes < 1048576) return string.Format("{0:N2} KB", (float)bytes / 1024F); else if(bytes < 1073741824) return string.Format("{0:N2} MB", (float)bytes / 1048576F); else return string.Format("{0:N2} GB", (float)bytes / 1073741824F); } /// /// Computes the Disk Space Usage of a directory. /// /// The directory. /// The used Disk Space, in bytes. public static long DiskUsage(string dir) { string[] files = Directory.GetFiles(dir); string[] directories = Directory.GetDirectories(dir); long result = 0; FileInfo file; for(int i = 0; i < files.Length; i++) { file = new FileInfo(files[i]); result += file.Length; } for(int i = 0; i < directories.Length; i++) { result += DiskUsage(directories[i]); } return result; } /// /// Generates the standard 5-digit Page Version string. /// /// The Page version. /// The 5-digit Version string. public static string GetVersionString(int version) { string result = version.ToString(); int len = result.Length; for(int i = 0; i < 5 - len; i++) { result = "0" + result; } return result; } /// /// Gets the available Themes. /// public static string[] AvailableThemes { get { string[] dirs = Directory.GetDirectories(Settings.ThemesDirectory); string[] res = new string[dirs.Length]; for(int i = 0; i < dirs.Length; i++) { //if(dirs[i].EndsWith("\\")) dirs[i] = dirs[i].Substring(0, dirs[i].Length - 1); dirs[i] = dirs[i].TrimEnd(Path.DirectorySeparatorChar); res[i] = dirs[i].Substring(dirs[i].LastIndexOf(Path.DirectorySeparatorChar) + 1); } return res; } } /// /// Gets the available Cultures. /// public static string[] AvailableCultures { get { // It seems, at least in VS 2008, that for Precompiled Web Sites, the GlobalResources pluginAssemblies that are not the // default resource (Culture=neutral), get sorted into subdirectories named by the Culture Info name. Every // assembly in these directories is called "ScrewTurn.Wiki.resources.dll" // I'm sure it's possible to just use the subdirectory names in the bin directory to get the culture info names, // however, I'm not sure what other things might get tossed in there by the compiler now or in the future. // That's why I'm specifically going for the App_GlobalResources.resources.dlls. // So, get all of the App_GlobalResources.resources.dll pluginAssemblies from bin and recurse subdirectories string[] dllFiles = Directory.GetFiles(Path.Combine(Settings.RootDirectory, "bin"), "ScrewTurn.Wiki.resources.dll", SearchOption.AllDirectories); // List to collect constructed culture names List cultureNames = new List(); // Manually add en-US culture CultureInfo enCI = new CultureInfo("en-US"); cultureNames.Add(enCI.Name + "|" + UppercaseInitial(enCI.NativeName) + " - " + enCI.EnglishName); // For every file we find // List format: xx-ZZ|Native name (English name) foreach(string s in dllFiles) { try { // Load a reflection only assembly from the filename Assembly asm = Assembly.ReflectionOnlyLoadFrom(s); // string for destructive parsing of the assembly's full name // Which, btw, looks something like this // App_GlobalResources.resources, Version=0.0.0.0, Culture=zh-cn, PublicKeyToken=null string fullName = asm.FullName; // Find the Culture= attribute int find = fullName.IndexOf("Culture="); // Remove it and everything prior fullName = fullName.Substring(find + 8); // Find the trailing comma find = fullName.IndexOf(','); // Remove it and everything after fullName = fullName.Substring(0, find); // Fullname should now be the culture info name and we can instantiate the CultureInfo class from it CultureInfo ci = new CultureInfo(fullName); // StringBuilders StringBuilder sb = new StringBuilder(); sb.Append(ci.Name); sb.Append("|"); sb.Append(UppercaseInitial(ci.NativeName)); sb.Append(" - "); sb.Append(ci.EnglishName); // Add the newly constructed Culture string cultureNames.Add(sb.ToString()); } catch(Exception ex) { Log.LogEntry("Error parsing culture info from " + s + Environment.NewLine + ex.Message, EntryType.Error, Log.SystemUsername); } } // If for whatever reason every one fails, this will return a 1 element array with the en-US info. cultureNames.Sort(); return cultureNames.ToArray(); } } private static string UppercaseInitial(string value) { if(value.Length > 0) { return value[0].ToString().ToUpper(CultureInfo.CurrentCulture) + value.Substring(1); } else return ""; } /// /// Computes the Hash of a Username, mixing it with other data, in order to avoid illegal Account activations. /// /// The Username. /// The email. /// The date/time. /// The secured Hash of the Username. public static string ComputeSecurityHash(string username, string email, DateTime dateTime) { return Hash.ComputeSecurityHash(username, email, dateTime, Settings.MasterPassword); } /// /// Escapes bad characters in a string (pipes and \n). /// /// The input string. /// The escaped string. public static string EscapeString(string input) { StringBuilder sb = new StringBuilder(input); sb.Replace("\r", ""); sb.Replace("\n", "%0A"); sb.Replace("|", "%7C"); return sb.ToString(); } /// /// Unescapes bad characters in a string (pipes and \n). /// /// The input string. /// The unescaped string. public static string UnescapeString(string input) { StringBuilder sb = new StringBuilder(input); sb.Replace("%7C", "|"); sb.Replace("%0A", "\n"); return sb.ToString(); } /// /// Generates a random 10-char Password. /// /// The Password. public static string GenerateRandomPassword() { Random r = new Random(); string password = ""; for(int i = 0; i < 10; i++) { if(i % 2 == 0) password += ((char)r.Next(65, 91)).ToString(); // Uppercase letter else password += ((char)r.Next(97, 123)).ToString(); // Lowercase letter } return password; } /// /// Gets the approximate System Uptime. /// public static TimeSpan SystemUptime { get { int t = Environment.TickCount; if(t < 0) t = t + int.MaxValue; t = t / 1000; return TimeSpan.FromSeconds(t); } } /// /// Converts a Time Span to string. /// /// The Time Span. /// The string. public static string TimeSpanToString(TimeSpan span) { string result = span.Days.ToString() + "d "; result += span.Hours.ToString() + "h "; result += span.Minutes.ToString() + "m "; result += span.Seconds.ToString() + "s"; return result; } /// /// Executes URL-encoding, avoiding to use '+' for spaces. /// /// The input string. /// The encoded string. public static string UrlEncode(string input) { return HttpContext.Current.Server.UrlEncode(input).Replace("+", "%20"); } /// /// Executes URL-decoding, replacing spaces as processed by UrlEncode. /// /// The input string. /// The decoded string. public static string UrlDecode(string input) { return HttpContext.Current.Server.UrlDecode(input.Replace("%20", " ")); } /// /// Removes all HTML tags from a text. /// /// The input HTML. /// The extracted plain text. public static string RemoveHtmlMarkup(string html) { StringBuilder sb = new StringBuilder(System.Text.RegularExpressions.Regex.Replace(html, "<[^>]*>", " ")); sb.Replace(" ", " "); sb.Replace(" ", " "); return sb.ToString(); } /// /// Extracts the directory name from a path used in the Files Storage Providers. /// /// The path, for example '/folder/blah/'. /// The directory name, for example 'blah'. public static string ExtractDirectoryName(string path) { path = path.Trim('/'); int idx = path.LastIndexOf("/"); return idx != -1 ? path.Substring(idx + 1) : path; } /// /// Detects the correct object associated to the current page using the Page and NS parameters in the query string. /// /// true to load the default page of the specified namespace when Page is not specified, false otherwise. /// If Page is specified and exists, the correct , otherwise null if loadDefault is false, /// or the object representing the default page of the specified namespace if loadDefault is true. public static PageInfo DetectCurrentPageInfo(bool loadDefault) { string nspace = HttpContext.Current.Request["NS"]; NamespaceInfo nsinfo = nspace != null ? Pages.FindNamespace(nspace) : null; string page = HttpContext.Current.Request["Page"]; if(string.IsNullOrEmpty(page)) { if(loadDefault) { if(nsinfo == null) page = Settings.DefaultPage; else page = nsinfo.DefaultPage != null ? nsinfo.DefaultPage.FullName : ""; } else return null; } string fullName = null; if(!page.StartsWith(nspace + ".")) fullName = nspace + "." + page; else fullName = page; fullName = fullName.Trim('.'); return Pages.FindPage(fullName); } /// /// Detects the full name of the current page using the Page and NS parameters in the query string. /// /// The full name of the page, regardless of the existence of the page. public static string DetectCurrentFullName() { string nspace = HttpContext.Current.Request["NS"] != null ? HttpContext.Current.Request["NS"] : ""; string page = HttpContext.Current.Request["Page"] != null ? HttpContext.Current.Request["Page"] : ""; string fullName = null; if(!page.StartsWith(nspace + ".")) fullName = nspace + "." + page; else fullName = page; return fullName.Trim('.'); } /// /// Detects the correct object associated to the current namespace using the NS parameter in the query string. /// /// The correct object, or null. public static NamespaceInfo DetectCurrentNamespaceInfo() { string nspace = HttpContext.Current.Request["NS"]; NamespaceInfo nsinfo = nspace != null ? Pages.FindNamespace(nspace) : null; return nsinfo; } /// /// Detects the name of the current namespace using the NS parameter in the query string. /// /// The name of the namespace, or an empty string. public static string DetectCurrentNamespace() { return HttpContext.Current.Request["NS"] != null ? HttpContext.Current.Request["NS"] : ""; } /// /// Gets the message ID for HTML anchors. /// /// The message date/time. /// The ID. public static string GetMessageIdForAnchor(DateTime messageDateTime) { return "MSG_" + messageDateTime.ToString("yyyyMMddHHmmss"); } /// /// Gets the name of a file's directory. /// /// The filename. /// The name of the item. public static string GetDirectoryName(string filename) { if(filename != null) { int index = filename.LastIndexOf("/"); if(index > 0) { string directoryName = filename.Substring(0, index + 1); if(!directoryName.StartsWith("/")) directoryName = "/" + directoryName; return directoryName; } } // Assume to navigate in the root directory return "/"; } /// /// Gets the update status of a component. /// /// The version file URL. /// The current version. /// The new version, if any. /// The URL of the new assembly, if applicable and available. /// The update status. /// This method only works in Full Trust. public static UpdateStatus GetUpdateStatus(string url, string currentVersion, out string newVersion, out string newAssemblyUrl) { // TODO: Verify usage of WebPermission class // http://msdn.microsoft.com/en-us/library/system.net.webpermission.aspx string urlHash = "UpdUrlCache-" + url.GetHashCode().ToString(); try { string ver = null; if(HttpContext.Current != null) { ver = HttpContext.Current.Cache[urlHash] as string; } if(ver == null) { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); req.AllowAutoRedirect = true; HttpWebResponse res = (HttpWebResponse)req.GetResponse(); if(res.StatusCode != HttpStatusCode.OK) { newVersion = null; newAssemblyUrl = null; return UpdateStatus.Error; } StreamReader sr = new StreamReader(res.GetResponseStream()); ver = sr.ReadToEnd(); sr.Close(); if(HttpContext.Current != null) { HttpContext.Current.Cache.Add(urlHash, ver, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null); } } string[] lines = ver.Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if(lines.Length == 0) { newVersion = null; newAssemblyUrl = null; return UpdateStatus.Error; } string[] versions = lines[0].Split('|'); bool upToDate = false; for(int i = 0; i < versions.Length; i++) { ver = versions[i]; if(versions[i].Equals(currentVersion)) { if(i == versions.Length - 1) upToDate = true; else upToDate = false; ver = versions[versions.Length - 1]; break; } } if(upToDate) { newVersion = null; newAssemblyUrl = null; return UpdateStatus.UpToDate; } else { newVersion = ver; if(lines.Length == 2) newAssemblyUrl = lines[1]; else newAssemblyUrl = null; return UpdateStatus.NewVersionFound; } } catch(Exception) { if(HttpContext.Current != null) { HttpContext.Current.Cache.Add(urlHash, "", null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null); } newVersion = null; newAssemblyUrl = null; return UpdateStatus.Error; } } /// /// Computes the hash value of a string that is value across application instances and versions. /// /// The string to compute the hash of. /// The hash value. public static uint HashDocumentNameForTemporaryIndex(string value) { if(value == null) throw new ArgumentNullException("value"); // sdbm algorithm, borrowed from http://www.cse.yorku.ca/~oz/hash.html uint hash = 0; foreach(char c in value) { // hash(i) = hash(i - 1) * 65599 + str[i] hash = c + (hash << 6) + (hash << 16) - hash; } return hash; } } /// /// Lists legal update statuses. /// public enum UpdateStatus { /// /// Error while retrieving version information. /// Error, /// /// The component is up-to-date. /// UpToDate, /// /// A new version was found. /// NewVersionFound } }