PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Utils.cs

#
C# | 1637 lines | 939 code | 200 blank | 498 comment | 117 complexity | 7eb62246c17e01ec4a097b8d1bcecb4b MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. namespace BlogEngine.Core
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Configuration;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Net;
  11. using System.Net.Mail;
  12. using System.Reflection;
  13. using System.Security;
  14. using System.Security.Cryptography;
  15. using System.Text;
  16. using System.Text.RegularExpressions;
  17. using System.Threading;
  18. using System.Web;
  19. using System.Web.Configuration;
  20. using System.Web.Hosting;
  21. using System.Web.UI;
  22. using System.Web.UI.WebControls;
  23. using System.Web.UI.HtmlControls;
  24. using System.Xml;
  25. using BlogEngine.Core.Web.Controls;
  26. using BlogEngine.Core.Web.Extensions;
  27. using System.Net.Sockets;
  28. /// <summary>
  29. /// Utilities for the entire solution to use.
  30. /// </summary>
  31. public static class Utils
  32. {
  33. #region Constants and Fields
  34. /// <summary>
  35. /// The cookie name for forcing the main theme on a mobile device.
  36. /// </summary>
  37. public const String ForceMainThemeCookieName = "forceMainTheme";
  38. /// <summary>
  39. /// The pattern.
  40. /// </summary>
  41. private const string Pattern = "<head.*<link( [^>]*title=\"{0}\"[^>]*)>.*</head>";
  42. /// <summary>
  43. /// The application's relative web root.
  44. /// </summary>
  45. private static string applicationRelativeWebRoot;
  46. /// <summary>
  47. /// The href regex.
  48. /// </summary>
  49. private static readonly Regex HrefRegex = new Regex(
  50. "href=\"(.*)\"", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  51. /// <summary>
  52. /// The regex between tags.
  53. /// </summary>
  54. private static readonly Regex RegexBetweenTags = new Regex(@">\s+", RegexOptions.Compiled);
  55. /// <summary>
  56. /// The regex line breaks.
  57. /// </summary>
  58. private static readonly Regex RegexLineBreaks = new Regex(@"\n\s+", RegexOptions.Compiled);
  59. /// <summary>
  60. /// The regex mobile.
  61. /// </summary>
  62. private static readonly Regex RegexMobile =
  63. new Regex(
  64. ConfigurationManager.AppSettings.Get("BlogEngine.MobileDevices"),
  65. RegexOptions.IgnoreCase | RegexOptions.Compiled);
  66. /// <summary>
  67. /// The regex strip html.
  68. /// </summary>
  69. private static readonly Regex RegexStripHtml = new Regex("<[^>]*>", RegexOptions.Compiled);
  70. /// <summary>
  71. /// Boolean for returning whether or not BlogEngine is currently running on Mono.
  72. /// </summary>
  73. private static readonly bool isMono = (Type.GetType("Mono.Runtime") != null);
  74. #endregion
  75. #region Events
  76. /// <summary>
  77. /// Occurs after an e-mail has been sent. The sender is the MailMessage object.
  78. /// </summary>
  79. public static event EventHandler<EventArgs> EmailFailed;
  80. /// <summary>
  81. /// Occurs after an e-mail has been sent. The sender is the MailMessage object.
  82. /// </summary>
  83. public static event EventHandler<EventArgs> EmailSent;
  84. /// <summary>
  85. /// Occurs when a message will be logged. The sender is a string containing the log message.
  86. /// </summary>
  87. public static event EventHandler<EventArgs> OnLog;
  88. #endregion
  89. #region Properties
  90. /// <summary>
  91. /// Gets the absolute root of the website.
  92. /// </summary>
  93. /// <value>A string that ends with a '/'.</value>
  94. public static Uri AbsoluteWebRoot
  95. {
  96. get
  97. {
  98. var context = HttpContext.Current;
  99. if (context == null)
  100. {
  101. throw new WebException("The current HttpContext is null");
  102. }
  103. var absoluteurl = context.Items["absoluteurl"];
  104. if (absoluteurl == null)
  105. {
  106. absoluteurl = new Uri(context.Request.Url.GetLeftPart(UriPartial.Authority) + RelativeWebRoot);
  107. context.Items["absoluteurl"] = absoluteurl;
  108. }
  109. return absoluteurl as Uri;
  110. }
  111. }
  112. /// <summary>
  113. /// Gets the relative URL of the blog feed. If a Feedburner username
  114. /// is entered in the admin settings page, it will return the
  115. /// absolute Feedburner URL to the feed.
  116. /// </summary>
  117. public static string FeedUrl
  118. {
  119. get
  120. {
  121. return !string.IsNullOrEmpty(BlogSettings.Instance.AlternateFeedUrl)
  122. ? BlogSettings.Instance.AlternateFeedUrl
  123. : string.Format("{0}syndication.axd", AbsoluteWebRoot);
  124. }
  125. }
  126. /// <summary>
  127. /// Gets a value indicating whether we're running under Linux or a Unix variant.
  128. /// </summary>
  129. /// <value><c>true</c> if Linux/Unix; otherwise, <c>false</c>.</value>
  130. public static bool IsLinux
  131. {
  132. get
  133. {
  134. var p = (int)Environment.OSVersion.Platform;
  135. return (p == 4) || (p == 128);
  136. }
  137. }
  138. /// <summary>
  139. /// Gets a value indicating whether the client is a mobile device.
  140. /// </summary>
  141. /// <value><c>true</c> if this instance is mobile; otherwise, <c>false</c>.</value>
  142. public static bool IsMobile
  143. {
  144. get
  145. {
  146. var context = HttpContext.Current;
  147. if (context != null)
  148. {
  149. var request = context.Request;
  150. if (request.Browser.IsMobileDevice)
  151. {
  152. return true;
  153. }
  154. if (!string.IsNullOrEmpty(request.UserAgent) && RegexMobile.IsMatch(request.UserAgent))
  155. {
  156. return true;
  157. }
  158. }
  159. return false;
  160. }
  161. }
  162. /// <summary>
  163. /// Gets a value indicating whether we're running under Mono.
  164. /// </summary>
  165. /// <value><c>true</c> if Mono; otherwise, <c>false</c>.</value>
  166. public static bool IsMono
  167. {
  168. get
  169. {
  170. return isMono;
  171. }
  172. }
  173. /// <summary>
  174. /// Gets the relative root of the current blog instance.
  175. /// </summary>
  176. /// <value>A string that ends with a '/'.</value>
  177. public static string RelativeWebRoot
  178. {
  179. get
  180. {
  181. return Blog.CurrentInstance.RelativeWebRoot;
  182. }
  183. }
  184. /// <summary>
  185. /// Gets the application's relative root.
  186. /// </summary>
  187. /// <value>A string that ends with a '/'.</value>
  188. public static string ApplicationRelativeWebRoot
  189. {
  190. get
  191. {
  192. return applicationRelativeWebRoot ??
  193. (applicationRelativeWebRoot =
  194. VirtualPathUtility.ToAbsolute(BlogConfig.VirtualPath));
  195. }
  196. }
  197. /// <summary>
  198. /// Gets if the current HTTP request is a homepage request, taking the blog
  199. /// instance into consideration.
  200. /// </summary>
  201. public static bool IsCurrentRequestForHomepage
  202. {
  203. get
  204. {
  205. HttpContext context = HttpContext.Current;
  206. // because the homepage uses querystring values to render non-homepage content
  207. // such as posts by tag, posts by date range, posts by author, etc, if there
  208. // are any QS parameters, this will be considered not a homepage request.
  209. if (context.Request.QueryString.Count != 0) { return false; }
  210. string path = context.Request.Path;
  211. // If this is a virtual folder for a blog instance, unless "default.aspx" is
  212. // actually in the URL, default.aspx won't be reported in path, so we check
  213. // to see if path is the root of RelativeWebRoot (the blog instance's
  214. // relative web root).
  215. if (RelativeWebRoot.Equals(VirtualPathUtility.AppendTrailingSlash(path), StringComparison.OrdinalIgnoreCase))
  216. return true;
  217. else if (path.Equals(string.Format("{0}default.aspx", RelativeWebRoot), StringComparison.OrdinalIgnoreCase))
  218. return true;
  219. return false;
  220. }
  221. }
  222. #endregion
  223. #region Public Methods
  224. /// <summary>
  225. /// Returns whether the main theme should be forced for the current mobile device.
  226. /// </summary>
  227. /// <param name="request">The request.</param>
  228. /// <returns>
  229. /// A <see cref="Boolean"/> instance.
  230. /// </returns>
  231. public static Boolean ShouldForceMainTheme(HttpRequest request)
  232. {
  233. var forceMainThemeCookie = request.Cookies[ForceMainThemeCookieName];
  234. if (forceMainThemeCookie == null)
  235. {
  236. return false;
  237. }
  238. if (String.IsNullOrEmpty(forceMainThemeCookie.Value))
  239. {
  240. return false;
  241. }
  242. Boolean forceMainTheme;
  243. if (Boolean.TryParse(forceMainThemeCookie.Value, out forceMainTheme))
  244. {
  245. return forceMainTheme;
  246. }
  247. return false;
  248. }
  249. /// <summary>
  250. /// Parse the string representation of an enum field to a strongly typed enum value.
  251. /// </summary>
  252. public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
  253. {
  254. if (!typeof(T).IsEnum)
  255. throw new ArgumentException("T must be an enumerated type");
  256. if (string.IsNullOrEmpty(value))
  257. return defaultValue;
  258. foreach (T item in Enum.GetValues(typeof(T)))
  259. {
  260. if (item.ToString().Equals(value, StringComparison.OrdinalIgnoreCase))
  261. return item;
  262. }
  263. return defaultValue;
  264. }
  265. private static Regex _identifierForDisplayRgx = new Regex(
  266. @" (?<=[A-Z])(?=[A-Z][a-z]) # UC before me, UC lc after me
  267. | (?<=[^A-Z])(?=[A-Z]) # Not UC before me, UC after me
  268. | (?<=[A-Za-z])(?=[^A-Za-z]) # Letter before me, non letter after me
  269. ", RegexOptions.IgnorePatternWhitespace
  270. );
  271. /// <summary>
  272. /// Format's an identifier (e.g. variable, enum field value) for display purposes,
  273. /// by adding a space before each capital letter. A value like EditOwnUser becomes
  274. /// "Edit Own User". If multiple capital letters are in identifier, these letters
  275. /// will be treated as one word (e.g. XMLEditor becomes "XML Editor", not
  276. /// "X M L Editor").
  277. /// </summary>
  278. /// <param name="fieldName">An identifier ready to be formatted for display.</param>
  279. /// <returns>The identifier for display purposes.</returns>
  280. /// <remarks>Credit: http://stackoverflow.com/questions/3103730</remarks>
  281. public static string FormatIdentifierForDisplay(string fieldName)
  282. {
  283. StringBuilder sb = new StringBuilder();
  284. foreach (string part in _identifierForDisplayRgx.Split(fieldName))
  285. {
  286. if (sb.Length > 0) { sb.Append(" "); }
  287. sb.Append(part);
  288. }
  289. return sb.ToString();
  290. }
  291. /// <summary>
  292. /// Selects a listitem by value, case insensitively.
  293. /// </summary>
  294. /// <param name="control">The ListControl</param>
  295. /// <param name="value">The value to select</param>
  296. /// <returns>The ListItem found and selected</returns>
  297. public static ListItem SelectListItemByValue(ListControl control, string value)
  298. {
  299. control.ClearSelection();
  300. control.SelectedIndex = -1;
  301. foreach (ListItem li in control.Items)
  302. {
  303. if (string.Equals(value, li.Value, StringComparison.OrdinalIgnoreCase))
  304. {
  305. li.Selected = true;
  306. return li;
  307. }
  308. }
  309. return null;
  310. }
  311. /// <summary>
  312. /// Converts an object to its JSON representation.
  313. /// </summary>
  314. /// <param name="obj"></param>
  315. /// <returns></returns>
  316. public static string ConvertToJson(object obj)
  317. {
  318. return new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(obj);
  319. }
  320. /// <summary>
  321. /// Resolves the script URL.
  322. /// </summary>
  323. /// <param name="url">
  324. /// The URL string.
  325. /// </param>
  326. /// <param name="minify"></param>
  327. /// <returns>
  328. /// The resolve script url.
  329. /// </returns>
  330. public static string ResolveScriptUrl(string url, bool minify)
  331. {
  332. var minifyQuery = (minify ? "&amp;minify=" : String.Empty);
  333. return string.Format("{0}js.axd?path={1}{2}",
  334. Utils.RelativeWebRoot,
  335. HttpUtility.UrlEncode(url),
  336. minifyQuery);
  337. }
  338. /// <summary>
  339. /// Adds a JavaScript reference to the HTML head tag.
  340. /// </summary>
  341. /// <param name="page">
  342. /// The page to add the JavaScript include to.
  343. /// </param>
  344. /// <param name="url">
  345. /// The url string.
  346. /// </param>
  347. /// <param name="placeInBottom">
  348. /// The place In Bottom.
  349. /// </param>
  350. /// <param name="addDeferAttribute">
  351. /// The add Defer Attribute.
  352. /// </param>
  353. public static void AddJavaScriptInclude(System.Web.UI.Page page, string url, bool placeInBottom, bool addDeferAttribute) {
  354. AddJavaScriptInclude(page, url, placeInBottom, addDeferAttribute, false);
  355. }
  356. /// <summary>
  357. /// Adds a JavaScript reference to the HTML head tag.
  358. /// </summary>
  359. /// <param name="page">
  360. /// The page to add the JavaScript include to.
  361. /// </param>
  362. /// <param name="url">
  363. /// The url string.
  364. /// </param>
  365. /// <param name="placeInBottom">
  366. /// The place In Bottom.
  367. /// </param>
  368. /// <param name="addDeferAttribute">
  369. /// The add Defer Attribute.
  370. /// </param>
  371. /// <param name="minify">Set to true if the minify querystring parameter should be added to this script.</param>
  372. public static void AddJavaScriptInclude(System.Web.UI.Page page, string url, bool placeInBottom, bool addDeferAttribute, bool minify)
  373. {
  374. if (placeInBottom)
  375. {
  376. var deferAttr = (addDeferAttribute ? " defer=\"defer\"" : string.Empty);
  377. var script = string.Format("<script type=\"text/javascript\"{0} src=\"{1}\"></script>", deferAttr, ResolveScriptUrl(url, minify));
  378. page.ClientScript.RegisterStartupScript(page.GetType(), url.GetHashCode().ToString(), script);
  379. }
  380. else
  381. {
  382. var script = new HtmlGenericControl("script");
  383. script.Attributes["type"] = "text/javascript";
  384. script.Attributes["src"] = ResolveScriptUrl(url,minify);
  385. if (addDeferAttribute)
  386. {
  387. script.Attributes["defer"] = "defer";
  388. }
  389. string itemsKey = "next-script-insert-position";
  390. HttpContext context = HttpContext.Current;
  391. // Inserting scripts in the beginning of the HEAD tag so any user
  392. // scripts that may rely on our scripts (jQuery, etc) have these
  393. // scripts available to them. Also maintaining order so subsequent
  394. // scripts are added after scripts we already added.
  395. int? nextInsertPosition = context == null ? null : context.Items[itemsKey] as int?;
  396. if (!nextInsertPosition.HasValue) { nextInsertPosition = 0; }
  397. page.Header.Controls.AddAt(nextInsertPosition.Value, script);
  398. if (context != null) { context.Items[itemsKey] = ++nextInsertPosition; }
  399. }
  400. }
  401. /// <summary>
  402. /// Represents a JS, CSS or other external content type.
  403. /// </summary>
  404. private sealed class ExternalContentItem
  405. {
  406. public string ItemName { get; set; }
  407. public int Priority { get; set; }
  408. public bool Defer { get; set; }
  409. public bool AddAtBottom { get; set; }
  410. public bool IsFrontEndOnly { get; set; }
  411. public bool Minify { get; set; }
  412. }
  413. /// <summary>
  414. /// Add JavaScript files from a folder.
  415. /// </summary>
  416. public static void AddFolderJavaScripts(
  417. System.Web.UI.Page page,
  418. string pathFromRoot,
  419. bool isFrontEnd)
  420. {
  421. // record that this script has been added during this request, so any
  422. // subsequent calls to AddFolderJavaScripts doesn't result in the same
  423. // script being added more than once.
  424. string itemsKey = "scripts-added-during-request";
  425. Dictionary<string, bool> scriptsAddedDuringRequest = HttpContext.Current.Items[itemsKey] as Dictionary<string, bool>;
  426. if (scriptsAddedDuringRequest == null)
  427. {
  428. scriptsAddedDuringRequest = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
  429. HttpContext.Current.Items[itemsKey] = scriptsAddedDuringRequest;
  430. }
  431. // common rules for sorting, etc.
  432. // Files that are already minified shouldn't be re-minified as it could cause problems.
  433. List<ExternalContentItem> knownItems = new List<ExternalContentItem>()
  434. {
  435. new ExternalContentItem() { ItemName = "jquery.js", Priority = 0, Defer = false, Minify=false },
  436. new ExternalContentItem() { ItemName = "blog.js", Priority = 1, Defer = false, IsFrontEndOnly = true, AddAtBottom = true, Minify = true },
  437. };
  438. Dictionary<string, ExternalContentItem> contentItems = knownItems.ToDictionary(i => i.ItemName, i => i, StringComparer.OrdinalIgnoreCase);
  439. // remove leading slash, if present
  440. if (pathFromRoot.StartsWith("/"))
  441. pathFromRoot = pathFromRoot.Substring(1);
  442. // remove trailing slash, if present.
  443. if (pathFromRoot.EndsWith("/"))
  444. pathFromRoot = pathFromRoot.Remove(pathFromRoot.Length - 1);
  445. var fileEntries = Directory.GetFiles(HostingEnvironment.MapPath("~/" + pathFromRoot))
  446. .Where(file =>
  447. !scriptsAddedDuringRequest.ContainsKey(file) &&
  448. file.EndsWith(".js", StringComparison.OrdinalIgnoreCase) &&
  449. !file.EndsWith("-vsdoc.js", StringComparison.OrdinalIgnoreCase)).ToList();
  450. fileEntries.Sort((x, y) =>
  451. {
  452. string xFile = Path.GetFileName(x);
  453. string yFile = Path.GetFileName(y);
  454. int xPriority = contentItems.ContainsKey(xFile) ? contentItems[xFile].Priority : int.MaxValue;
  455. int yPriority = contentItems.ContainsKey(yFile) ? contentItems[yFile].Priority : int.MaxValue;
  456. if (xPriority == yPriority)
  457. return xFile.CompareTo(yFile);
  458. else
  459. return xPriority.CompareTo(yPriority);
  460. });
  461. foreach (var file in fileEntries)
  462. {
  463. string fileName = Utils.ExtractFileNameFromPath(file);
  464. ExternalContentItem externalContentItem = contentItems.ContainsKey(fileName) ? contentItems[fileName] : null;
  465. bool defer = false;
  466. bool addItem = true;
  467. bool addAtBottom = false;
  468. bool minify = false;
  469. if (externalContentItem != null)
  470. {
  471. defer = externalContentItem.Defer;
  472. addAtBottom = externalContentItem.AddAtBottom;
  473. minify = externalContentItem.Minify;
  474. if (externalContentItem.IsFrontEndOnly && !isFrontEnd)
  475. {
  476. addItem = false;
  477. }
  478. }
  479. if (addItem)
  480. {
  481. scriptsAddedDuringRequest.Add(file, true);
  482. AddJavaScriptInclude(page,
  483. string.Format("{0}{1}/{2}", Utils.ApplicationRelativeWebRoot, pathFromRoot, fileName), addAtBottom, defer, minify);
  484. }
  485. }
  486. }
  487. /// <summary>
  488. /// This method adds the resource handler script responsible for loading up BlogEngine's culture-specific resource strings
  489. /// on the client side. This needs to be called at a point after blog.js has been added to the page, otherwise this script
  490. /// will fail at load time.
  491. /// </summary>
  492. /// <param name="page">The System.Web.UI.Page instance the resources will be added to.</param>
  493. public static void AddJavaScriptResourcesToPage(System.Web.UI.Page page)
  494. {
  495. var resourcePath = Web.HttpHandlers.ResourceHandler.GetScriptPath(new CultureInfo(BlogSettings.Instance.Language));
  496. var script = string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>", resourcePath);
  497. page.ClientScript.RegisterStartupScript(page.GetType(), resourcePath.GetHashCode().ToString(), script);
  498. }
  499. /// <summary>
  500. /// This method returns all code assemblies in app_code
  501. /// If app_code has subdirectories for c#, vb.net etc
  502. /// Each one will come back as a separate assembly
  503. /// So we can support extensions in multiple languages
  504. /// </summary>
  505. /// <returns>
  506. /// List of code assemblies
  507. /// </returns>
  508. public static IEnumerable<Assembly> CodeAssemblies()
  509. {
  510. var codeAssemblies = new List<Assembly>();
  511. CompilationSection s = null;
  512. var assemblyName = "__code";
  513. try
  514. {
  515. try
  516. {
  517. s = (CompilationSection)WebConfigurationManager.GetSection("system.web/compilation");
  518. }
  519. catch (SecurityException)
  520. {
  521. // No read permissions on web.config due to the trust level (must be High or Full)
  522. }
  523. if (s != null && s.CodeSubDirectories != null && s.CodeSubDirectories.Count > 0)
  524. {
  525. for (var i = 0; i < s.CodeSubDirectories.Count; i++)
  526. {
  527. assemblyName = string.Format("App_SubCode_{0}", s.CodeSubDirectories[i].DirectoryName);
  528. codeAssemblies.Add(Assembly.Load(assemblyName));
  529. }
  530. }
  531. else
  532. {
  533. assemblyName = "App_Code";
  534. codeAssemblies.Add(Assembly.Load(assemblyName));
  535. }
  536. }
  537. catch (FileNotFoundException)
  538. {
  539. /*ignore - code directory has no files*/
  540. }
  541. try
  542. {
  543. codeAssemblies.AddRange(GetCompiledExtensions());
  544. }
  545. catch (Exception ex)
  546. {
  547. Log("Error loading compiled assemblies: " + ex.Message);
  548. }
  549. return codeAssemblies;
  550. }
  551. /// <summary>
  552. /// Converts a relative URL to an absolute one.
  553. /// </summary>
  554. /// <param name="relativeUri">
  555. /// The relative Uri.
  556. /// </param>
  557. /// <returns>
  558. /// The absolute Uri.
  559. /// </returns>
  560. public static Uri ConvertToAbsolute(Uri relativeUri)
  561. {
  562. return ConvertToAbsolute(relativeUri.ToString());
  563. }
  564. /// <summary>
  565. /// Converts a relative URL to an absolute one.
  566. /// </summary>
  567. /// <param name="relativeUri">
  568. /// The relative Uri.
  569. /// </param>
  570. /// <returns>
  571. /// The absolute Uri.
  572. /// </returns>
  573. public static Uri ConvertToAbsolute(string relativeUri)
  574. {
  575. if (String.IsNullOrEmpty(relativeUri))
  576. {
  577. throw new ArgumentNullException("relativeUri");
  578. }
  579. var absolute = AbsoluteWebRoot.ToString();
  580. var index = absolute.LastIndexOf(RelativeWebRoot);
  581. return new Uri(absolute.Substring(0, index) + relativeUri);
  582. }
  583. /// <summary>
  584. /// Downloads a web page from the Internet and returns the HTML as a string. .
  585. /// </summary>
  586. /// <param name="url">
  587. /// The URL to download from.
  588. /// </param>
  589. /// <returns>
  590. /// The HTML or null if the URL isn't valid.
  591. /// </returns>
  592. [Obsolete("Use the RemoteFile class instead.")]
  593. public static string DownloadWebPage(Uri url)
  594. {
  595. try
  596. {
  597. var remoteFile = new RemoteFile(url, true);
  598. return remoteFile.GetFileAsString();
  599. }
  600. catch (Exception)
  601. {
  602. return null;
  603. }
  604. }
  605. /// <summary>
  606. /// Extract file name from given physical server path
  607. /// </summary>
  608. /// <param name="path">
  609. /// The Server path.
  610. /// </param>
  611. /// <returns>
  612. /// The File Name.
  613. /// </returns>
  614. public static string ExtractFileNameFromPath(string path)
  615. {
  616. return Path.GetFileName(path);
  617. }
  618. /// <summary>
  619. /// Finds semantic links in a given HTML document.
  620. /// </summary>
  621. /// <param name="type">
  622. /// The type of link. Could be foaf, apml or sioc.
  623. /// </param>
  624. /// <param name="html">
  625. /// The HTML to look through.
  626. /// </param>
  627. /// <returns>
  628. /// A list of Uri.
  629. /// </returns>
  630. public static List<Uri> FindLinks(string type, string html)
  631. {
  632. var matches = Regex.Matches(
  633. html, string.Format(Pattern, type), RegexOptions.IgnoreCase | RegexOptions.Singleline);
  634. var urls = new List<Uri>();
  635. foreach (var hrefMatch in
  636. matches.Cast<Match>().Where(match => match.Groups.Count == 2).Select(match => match.Groups[1].Value).
  637. Select(link => HrefRegex.Match(link)).Where(hrefMatch => hrefMatch.Groups.Count == 2))
  638. {
  639. Uri url;
  640. var value = hrefMatch.Groups[1].Value;
  641. if (Uri.TryCreate(value, UriKind.Absolute, out url))
  642. {
  643. urls.Add(url);
  644. }
  645. }
  646. return urls;
  647. }
  648. /// <summary>
  649. /// Finds the semantic documents from a URL based on the type.
  650. /// </summary>
  651. /// <param name="url">
  652. /// The URL of the semantic document or a document containing semantic links.
  653. /// </param>
  654. /// <param name="type">
  655. /// The type. Could be "foaf", "apml" or "sioc".
  656. /// </param>
  657. /// <returns>
  658. /// A dictionary of the semantic documents. The dictionary is empty if no documents were found.
  659. /// </returns>
  660. public static Dictionary<Uri, XmlDocument> FindSemanticDocuments(Uri url, string type)
  661. {
  662. var list = new Dictionary<Uri, XmlDocument>();
  663. // ignoreRemoteDownloadSettings is set to true for the
  664. // RemoteFile instance for backwards compatibility.
  665. var remoteFile = new RemoteFile(url, true);
  666. var content = remoteFile.GetFileAsString();
  667. if (!string.IsNullOrEmpty(content))
  668. {
  669. var upper = content.ToUpperInvariant();
  670. if (upper.Contains("</HEAD") && upper.Contains("</HTML"))
  671. {
  672. var urls = FindLinks(type, content);
  673. foreach (var xmlUrl in urls)
  674. {
  675. var doc = LoadDocument(url, xmlUrl);
  676. if (doc != null)
  677. {
  678. list.Add(xmlUrl, doc);
  679. }
  680. }
  681. }
  682. else
  683. {
  684. var doc = LoadDocument(url, url);
  685. if (doc != null)
  686. {
  687. list.Add(url, doc);
  688. }
  689. }
  690. }
  691. return list;
  692. }
  693. /// <summary>
  694. /// Returns the default culture. This is either the culture specified in the blog settings,
  695. /// or the default culture installed with the operating system.
  696. /// </summary>
  697. /// <returns>
  698. /// The default culture.
  699. /// </returns>
  700. public static CultureInfo GetDefaultCulture()
  701. {
  702. var settingsCulture = BlogSettings.Instance.Culture;
  703. if (Utils.StringIsNullOrWhitespace(settingsCulture) ||
  704. settingsCulture.Equals("Auto", StringComparison.OrdinalIgnoreCase))
  705. {
  706. return CultureInfo.InstalledUICulture;
  707. }
  708. return CultureInfo.CreateSpecificCulture(settingsCulture);
  709. }
  710. /// <summary>
  711. /// Gets the sub domain.
  712. /// </summary>
  713. /// <param name="url">
  714. /// The URL to get the sub domain from.
  715. /// </param>
  716. /// <returns>
  717. /// The sub domain.
  718. /// </returns>
  719. public static string GetSubDomain(Uri url)
  720. {
  721. if (url.HostNameType == UriHostNameType.Dns)
  722. {
  723. var host = url.Host;
  724. if (host.Split('.').Length > 2)
  725. {
  726. var lastIndex = host.LastIndexOf(".");
  727. var index = host.LastIndexOf(".", lastIndex - 1);
  728. return host.Substring(0, index);
  729. }
  730. }
  731. return null;
  732. }
  733. /// <summary>
  734. /// Encrypts a string using the SHA256 algorithm.
  735. /// </summary>
  736. /// <param name="plainMessage">
  737. /// The plain Message.
  738. /// </param>
  739. /// <returns>
  740. /// The hash password.
  741. /// </returns>
  742. public static string HashPassword(string plainMessage)
  743. {
  744. var data = Encoding.UTF8.GetBytes(plainMessage);
  745. using (HashAlgorithm sha = new SHA256Managed())
  746. {
  747. sha.TransformFinalBlock(data, 0, data.Length);
  748. return Convert.ToBase64String(sha.Hash);
  749. }
  750. }
  751. /// <summary>
  752. /// The body regex.
  753. /// </summary>
  754. private static readonly Regex BodyRegex = new Regex(@"\[UserControl:(.*?)\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
  755. /// <summary>
  756. /// Injects any user controls if one is referenced in the text.
  757. /// </summary>
  758. /// <param name="containerControl">The control that the user controls will be injected in to.</param>
  759. /// <param name="content">The string to parse for user controls.</param>
  760. public static void InjectUserControls(System.Web.UI.Control containerControl, string content)
  761. {
  762. var currentPosition = 0;
  763. var matches = BodyRegex.Matches(content);
  764. var containerControls = containerControl.Controls;
  765. var page = containerControl.Page;
  766. foreach (Match match in matches)
  767. {
  768. // Add literal for content before custom tag should it exist.
  769. if (match.Index > currentPosition)
  770. {
  771. containerControls.Add(new LiteralControl(content.Substring(currentPosition, match.Index - currentPosition)));
  772. }
  773. // Now lets add our user control.
  774. try
  775. {
  776. var all = match.Groups[1].Value.Trim();
  777. Control usercontrol;
  778. if (!all.EndsWith(".ascx", StringComparison.OrdinalIgnoreCase))
  779. {
  780. var index = all.IndexOf(".ascx", StringComparison.OrdinalIgnoreCase) + 5;
  781. usercontrol = page.LoadControl(all.Substring(0, index));
  782. var parameters = page.Server.HtmlDecode(all.Substring(index));
  783. var type = usercontrol.GetType();
  784. var paramCollection = parameters.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
  785. foreach (var param in paramCollection)
  786. {
  787. var paramSplit = param.Split('=');
  788. var name = paramSplit[0].Trim();
  789. var value = paramSplit[1].Trim();
  790. var property = type.GetProperty(name);
  791. property.SetValue(
  792. usercontrol,
  793. Convert.ChangeType(value, property.PropertyType, CultureInfo.InvariantCulture),
  794. null);
  795. }
  796. }
  797. else
  798. {
  799. usercontrol = page.LoadControl(all);
  800. }
  801. containerControls.Add(usercontrol);
  802. // Now we will update our position.
  803. // currentPosition = myMatch.Index + myMatch.Groups[0].Length;
  804. }
  805. catch (Exception)
  806. {
  807. // Whoopss, can't load that control so lets output something that tells the developer that theres a problem.
  808. containerControls.Add(
  809. new LiteralControl(string.Format("ERROR - UNABLE TO LOAD CONTROL : {0}", match.Groups[1].Value)));
  810. }
  811. currentPosition = match.Index + match.Groups[0].Length;
  812. }
  813. // Finally we add any trailing static text.
  814. containerControls.Add(
  815. new LiteralControl(content.Substring(currentPosition, content.Length - currentPosition)));
  816. }
  817. private static readonly Regex emailRegex = new Regex(
  818. @"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", RegexOptions.IgnoreCase);
  819. private static readonly Regex validIpV4AddressRegex = new Regex(@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", RegexOptions.IgnoreCase);
  820. private static readonly Regex validHostnameRegex = new Regex(@"^(([a-z]|[a-z][a-z0-9\-]*[a-z0-9])\.)*([a-z]|[a-z][a-z0-9\-]*[a-z0-9])$", RegexOptions.IgnoreCase);
  821. /// <summary>
  822. /// Email address by user name
  823. /// </summary>
  824. /// <param name="userName">User name</param>
  825. /// <returns>Email Address</returns>
  826. public static string GetUserEmail(string userName)
  827. {
  828. int count;
  829. var userCollection = System.Web.Security.Membership.Provider.GetAllUsers(0, 999, out count);
  830. var users = userCollection.Cast<System.Web.Security.MembershipUser>().ToList();
  831. var user = users.FirstOrDefault(u => u.UserName.Equals(userName));
  832. if(user != null)
  833. {
  834. return user.Email;
  835. }
  836. return userName;
  837. }
  838. /// <summary>
  839. /// Validates an email address.
  840. /// </summary>
  841. /// <param name="email"></param>
  842. /// <returns></returns>
  843. public static bool IsEmailValid(string email)
  844. {
  845. if (!string.IsNullOrWhiteSpace(email))
  846. {
  847. return emailRegex.IsMatch(email.Trim());
  848. }
  849. return false;
  850. }
  851. /// <summary>
  852. /// Validates a host name.
  853. /// </summary>
  854. /// <param name="hostname"></param>
  855. /// <returns></returns>
  856. public static bool IsHostnameValid(string hostname)
  857. {
  858. if (!string.IsNullOrWhiteSpace(hostname))
  859. {
  860. return validHostnameRegex.IsMatch(hostname.Trim());
  861. }
  862. return false;
  863. }
  864. /// <summary>
  865. /// Validates an IPv4 address.
  866. /// </summary>
  867. /// <param name="address"></param>
  868. /// <returns></returns>
  869. public static bool IsIpV4AddressValid(string address)
  870. {
  871. if (!string.IsNullOrWhiteSpace(address))
  872. {
  873. return validIpV4AddressRegex.IsMatch(address.Trim());
  874. }
  875. return false;
  876. }
  877. /// <summary>
  878. /// Validates an IPv6 address.
  879. /// </summary>
  880. /// <param name="address"></param>
  881. /// <returns></returns>
  882. public static bool IsIpV6AddressValid(string address)
  883. {
  884. if (!string.IsNullOrWhiteSpace(address))
  885. {
  886. IPAddress ip;
  887. if (IPAddress.TryParse(address, out ip))
  888. {
  889. return ip.AddressFamily == AddressFamily.InterNetworkV6;
  890. }
  891. }
  892. return false;
  893. }
  894. /// <summary>
  895. /// Run through all code assemblies and creates object
  896. /// instance for types marked with extension attribute
  897. /// </summary>
  898. public static void LoadExtensions()
  899. {
  900. var codeAssemblies = CodeAssemblies();
  901. var sortedExtensions = new List<SortedExtension>();
  902. foreach (Assembly a in codeAssemblies)
  903. {
  904. var types = a.GetTypes();
  905. Type extensionsAttribute = typeof(ExtensionAttribute);
  906. sortedExtensions.AddRange(
  907. from type in types
  908. let attributes = type.GetCustomAttributes(extensionsAttribute, false)
  909. from attribute in attributes
  910. where (attribute.GetType() == extensionsAttribute)
  911. let ext = (ExtensionAttribute)attribute
  912. select new SortedExtension(ext.Priority, type.Name, type.FullName));
  913. sortedExtensions.Sort(
  914. (e1, e2) =>
  915. e1.Priority == e2.Priority
  916. ? string.CompareOrdinal(e1.Name, e2.Name)
  917. : e1.Priority.CompareTo(e2.Priority));
  918. foreach (var x in sortedExtensions.Where(x => ExtensionManager.ExtensionEnabled(x.Name)))
  919. {
  920. a.CreateInstance(x.Type);
  921. }
  922. }
  923. // initialize comment rules and filters
  924. CommentHandlers.Listen();
  925. }
  926. /// <summary>
  927. /// Sends a message to any subscribed log listeners.
  928. /// </summary>
  929. /// <param name="message">
  930. /// The message to be logged.
  931. /// </param>
  932. public static void Log(object message)
  933. {
  934. if (OnLog != null)
  935. {
  936. OnLog(message, new EventArgs());
  937. }
  938. }
  939. /// <summary>
  940. /// Sends a message to any subscribed log listeners.
  941. /// </summary>
  942. /// <param name="methodName"></param>
  943. /// <param name="ex"></param>
  944. public static void Log(string methodName, Exception ex)
  945. {
  946. Log(String.Format("{0}: {1}", methodName, ex.Message));
  947. }
  948. /// <summary>
  949. /// Generates random password for password reset
  950. /// </summary>
  951. /// <returns>
  952. /// Random password
  953. /// </returns>
  954. public static string RandomPassword()
  955. {
  956. var chars = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
  957. var password = string.Empty;
  958. var random = new Random();
  959. for (var i = 0; i < 8; i++)
  960. {
  961. var x = random.Next(1, chars.Length);
  962. if (!password.Contains(chars.GetValue(x).ToString()))
  963. {
  964. password += chars.GetValue(x);
  965. }
  966. else
  967. {
  968. i--;
  969. }
  970. }
  971. return password;
  972. }
  973. /// <summary>
  974. /// Removes the HTML whitespace.
  975. /// </summary>
  976. /// <param name="html">
  977. /// The HTML to remove the whitespace from.
  978. /// </param>
  979. /// <returns>
  980. /// The html with the whitespace removed.
  981. /// </returns>
  982. public static string RemoveHtmlWhitespace(string html)
  983. {
  984. if (string.IsNullOrEmpty(html))
  985. {
  986. return string.Empty;
  987. }
  988. html = RegexBetweenTags.Replace(html, "> ");
  989. html = RegexLineBreaks.Replace(html, string.Empty);
  990. return html.Trim();
  991. }
  992. /// <summary>
  993. /// Renders a control to a string.
  994. /// </summary>
  995. /// <param name="control"></param>
  996. /// <returns></returns>
  997. public static string RenderControl(System.Web.UI.Control control)
  998. {
  999. if (control == null)
  1000. {
  1001. throw new ArgumentNullException("control");
  1002. }
  1003. using (var sWriter = new System.IO.StringWriter())
  1004. {
  1005. using (var hWriter = new System.Web.UI.HtmlTextWriter(sWriter))
  1006. {
  1007. control.RenderControl(hWriter);
  1008. }
  1009. return sWriter.ToString();
  1010. }
  1011. }
  1012. /// <summary>
  1013. /// Strips all illegal characters from the specified title.
  1014. /// </summary>
  1015. /// <param name="text">
  1016. /// The text to strip.
  1017. /// </param>
  1018. /// <returns>
  1019. /// The remove illegal characters.
  1020. /// </returns>
  1021. public static string RemoveIllegalCharacters(string text)
  1022. {
  1023. if (string.IsNullOrEmpty(text))
  1024. {
  1025. return text;
  1026. }
  1027. text = text.Replace(":", string.Empty);
  1028. text = text.Replace("/", string.Empty);
  1029. text = text.Replace("?", string.Empty);
  1030. text = text.Replace("#", string.Empty);
  1031. text = text.Replace("[", string.Empty);
  1032. text = text.Replace("]", string.Empty);
  1033. text = text.Replace("@", string.Empty);
  1034. text = text.Replace("*", string.Empty);
  1035. text = text.Replace(".", string.Empty);
  1036. text = text.Replace(",", string.Empty);
  1037. text = text.Replace("\"", string.Empty);
  1038. text = text.Replace("&", string.Empty);
  1039. text = text.Replace("'", string.Empty);
  1040. text = text.Replace(" ", "-");
  1041. text = RemoveDiacritics(text);
  1042. text = RemoveExtraHyphen(text);
  1043. return HttpUtility.HtmlEncode(text).Replace("%", string.Empty);
  1044. }
  1045. /// <summary>
  1046. /// Sends a MailMessage object using the SMTP settings.
  1047. /// </summary>
  1048. /// <param name="message">
  1049. /// The message.
  1050. /// </param>
  1051. /// <returns>
  1052. /// Error message, if any.
  1053. /// </returns>
  1054. public static string SendMailMessage(MailMessage message)
  1055. {
  1056. if (message == null)
  1057. {
  1058. throw new ArgumentNullException("message");
  1059. }
  1060. StringBuilder errorMsg = new StringBuilder();
  1061. try
  1062. {
  1063. message.IsBodyHtml = true;
  1064. message.BodyEncoding = Encoding.UTF8;
  1065. var smtp = new SmtpClient(BlogSettings.Instance.SmtpServer);
  1066. // don't send credentials if a server doesn't require it,
  1067. // linux smtp servers don't like that
  1068. if (!string.IsNullOrEmpty(BlogSettings.Instance.SmtpUserName))
  1069. {
  1070. smtp.Credentials = new NetworkCredential(
  1071. BlogSettings.Instance.SmtpUserName, BlogSettings.Instance.SmtpPassword);
  1072. }
  1073. smtp.Port = BlogSettings.Instance.SmtpServerPort;
  1074. smtp.EnableSsl = BlogSettings.Instance.EnableSsl;
  1075. smtp.Send(message);
  1076. OnEmailSent(message);
  1077. }
  1078. catch (Exception ex)
  1079. {
  1080. OnEmailFailed(message);
  1081. errorMsg.Append("Error sending email in SendMailMessage: ");
  1082. Exception current = ex;
  1083. while (current != null)
  1084. {
  1085. if (errorMsg.Length > 0) { errorMsg.Append(" "); }
  1086. errorMsg.Append(current.Message);
  1087. current = current.InnerException;
  1088. }
  1089. Utils.Log(errorMsg.ToString());
  1090. }
  1091. finally
  1092. {
  1093. // Remove the pointer to the message object so the GC can close the thread.
  1094. message.Dispose();
  1095. }
  1096. return errorMsg.ToString();
  1097. }
  1098. /// <summary>
  1099. /// Sends the mail message asynchronously in another thread.
  1100. /// </summary>
  1101. /// <param name="message">
  1102. /// The message to send.
  1103. /// </param>
  1104. public static void SendMailMessageAsync(MailMessage message)
  1105. {
  1106. // Before entering a BG thread, retrieve the current instance blog settings.
  1107. Guid blogId = Blog.CurrentInstance.Id;
  1108. ThreadPool.QueueUserWorkItem(delegate
  1109. {
  1110. // because HttpContext is not available within this BG thread
  1111. // needed to determine the current blog instance,
  1112. // set override value here.
  1113. Blog.InstanceIdOverride = blogId;
  1114. SendMailMessage(message);
  1115. });
  1116. }
  1117. /// <summary>
  1118. /// Writes ETag and Last-Modified headers and sets the conditional get headers.
  1119. /// </summary>
  1120. /// <param name="date">
  1121. /// The date for the headers.
  1122. /// </param>
  1123. /// <returns>
  1124. /// The set conditional get headers.
  1125. /// </returns>
  1126. public static bool SetConditionalGetHeaders(DateTime date)
  1127. {
  1128. // SetLastModified() below will throw an error if the 'date' is a future date.
  1129. // If the date is 1/1/0001, Mono will throw a 404 error
  1130. if (date > DateTime.Now || date.Year < 1900)
  1131. {
  1132. date = DateTime.Now;
  1133. }
  1134. var response = HttpContext.Current.Response;
  1135. var request = HttpContext.Current.Request;
  1136. var etag = string.Format("\"{0}\"", date.Ticks);
  1137. var incomingEtag = request.Headers["If-None-Match"];
  1138. DateTime incomingLastModifiedDate;
  1139. DateTime.TryParse(request.Headers["If-Modified-Since"], out incomingLastModifiedDate);
  1140. response.Cache.SetLastModified(date);
  1141. response.Cache.SetCacheability(HttpCacheability.Public);
  1142. response.Cache.SetETag(etag);
  1143. if (String.Compare(incomingEtag, etag) == 0 || incomingLastModifiedDate == date)
  1144. {
  1145. response.Clear();
  1146. response.StatusCode = (int)HttpStatusCode.NotModified;
  1147. return true;
  1148. }
  1149. return false;
  1150. }
  1151. /// <summary>
  1152. /// Returns whether a string is null, empty, or whitespace. Same implementation as in String.IsNullOrWhitespace in .Net 4.0
  1153. /// </summary>
  1154. /// <param name="value"></param>
  1155. /// <returns></returns>
  1156. public static bool StringIsNullOrWhitespace(string value)
  1157. {
  1158. return ((value == null) || (value.Trim().Length == 0));
  1159. }
  1160. /// <summary>
  1161. /// Strips all HTML tags from the specified…

Large files files are truncated, but you can click here to view the full file