using System; using System.ComponentModel; using System.Diagnostics; using System.Threading; using System.Web; using umbraco.BusinessLogic; using System.Collections.Generic; using umbraco.presentation.ClientDependency; namespace umbraco.presentation { /// /// Summary description for requestModule. /// public class requestModule : IHttpModule { protected Timer publishingTimer; protected Timer pingTimer; private HttpApplication mApp; private IContainer components = null; /// True if the module is currently handling an error. private static object handlingError = false; /// List of errors that occurred since the last error was being handled. private List unhandledErrors = new List(); public const string ORIGINAL_URL_CXT_KEY = "umbOriginalUrl"; protected void ApplicationStart(HttpApplication HttpApp) { //starting the application. Application doesn't support HttpContext in integrated mode (standard mode on IIS7) //So everything is moved to beginRequest. } protected void Application_PostAuthorizeRequest(object sender, EventArgs e) { // process rewrite here so forms authentication can Authorize based on url before the original url is discarded this.UmbracoRewrite(sender, e); } protected void Application_AuthorizeRequest(object sender, EventArgs e) { // nothing needs to be done here } protected void Application_PreRequestHandlerExecute(object sender, EventArgs e) { HttpContext context = mApp.Context; //httpContext.RewritePath( (string) httpContext.Items[ORIGINAL_URL_CXT_KEY] + "?" + httpContext.Request.QueryString ); } /// /// Handles the BeginRequest event of the Application control. /// /// The source of the event. /// The instance containing the event data. protected void Application_BeginRequest(Object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; //first time init, starts timers, and sets httpContext InitializeApplication(app); // grab the original url before anything modifies it HttpContext httpContext = mApp.Context; httpContext.Items[ORIGINAL_URL_CXT_KEY] = rewrite404Url(httpContext.Request.Url.AbsolutePath, httpContext.Request.Url.Query, false); // create the Umbraco context UmbracoContext.Current = new UmbracoContext(httpContext); // rewrite will happen after authorization } protected string rewrite404Url(string url, string querystring, bool returnQuery) { // adding endswith and contains checks to ensure support for custom 404 messages (only 404 parse directory and aspx requests) if (querystring.StartsWith("?404") && (!querystring.Contains(".") || querystring.EndsWith(".aspx") || querystring.Contains(".aspx&"))) { Uri u = new Uri(querystring.Substring(5, querystring.Length-5)); string path = u.AbsolutePath; if (returnQuery) { return u.Query; } else { return path; } } if (returnQuery) { return querystring; } else { return url; } } /// /// Performs path rewriting. /// /// The source of the event. /// The instance containing the event data. protected void UmbracoRewrite(Object sender, EventArgs e) { HttpContext context = mApp.Context; string path = rewrite404Url(context.Request.Path.ToLower(), context.Request.Url.Query, false); string query = String.Empty; if (GlobalSettings.UseDirectoryUrls) { int asmxPos = path.IndexOf(".asmx/"); if (asmxPos >= 0) context.RewritePath(path.Substring(0, asmxPos + 5), path.Substring(asmxPos + 5), context.Request.QueryString.ToString()); } if (path.IndexOf(".aspx") > -1 || path.IndexOf('.') == -1) { // validate configuration if (mApp.Application["umbracoNeedConfiguration"] == null) mApp.Application["umbracoNeedConfiguration"] = !GlobalSettings.Configured; if (!GlobalSettings.IsReservedPathOrUrl(path)) { // redirect if Umbraco needs configuration Nullable needsConfiguration = (Nullable)mApp.Application["umbracoNeedConfiguration"]; if (needsConfiguration.HasValue && needsConfiguration.Value) context.Response.Redirect(string.Format("{0}/../install/default.aspx?redir=true&url={1}", GlobalSettings.Path, context.Request.Path.ToLower()), true); // show splash? else if (UmbracoSettings.EnableSplashWhileLoading && content.Instance.isInitializing) context.RewritePath(string.Format("{0}/../config/splashes/booting.aspx", GlobalSettings.Path)); // rewrite page path else { string receivedQuery = rewrite404Url(context.Request.Path.ToLower(), context.Request.Url.Query, true); if (receivedQuery.Length > 0) { // Clean umbPage from querystring, caused by .NET 2.0 default Auth Controls if (receivedQuery.IndexOf("umbPage") > 0) { int ampPos = receivedQuery.IndexOf('&'); // query contains no ampersand? if (ampPos < 0) { // no ampersand means no original query string query = String.Empty; // ampersand would occur past then end the of received query ampPos = receivedQuery.Length; } else { // original query string past ampersand query = receivedQuery.Substring(ampPos + 1, receivedQuery.Length - ampPos - 1); } // get umbPage out of query string (9 = "&umbPage".Length() + 1) path = receivedQuery.Substring(9, ampPos - 9); } else { // strip off question mark query = receivedQuery.Substring(1); } } // save original URL context.Items["UmbPage"] = path; context.Items["VirtualUrl"] = String.Format("{0}?{1}", path, query); // rewrite to the new URL context.RewritePath(string.Format("{0}/../default.aspx?{2}", GlobalSettings.Path, path, query)); } } } } /// /// Handles the Error event of the Application control. /// /// The source of the event. /// The instance containing the event data. protected void Application_Error(Object sender, EventArgs e) { // return immediately if an error is already been handled, to avoid infinite recursion if ((bool)handlingError) { lock (unhandledErrors) { unhandledErrors.Add(mApp.Server.GetLastError()); } return; } // make sure only one thread at a time can handle an error lock (handlingError) { Debug.Assert(!(bool)handlingError, "Two errors are being handled at the same time."); handlingError = true; // make sure handlingError always gets set to false try { if (GlobalSettings.Configured) { // log the error string error; if (mApp.Context.Request != null) error = string.Format("At {0} (Referred by: {1}): {2}", mApp.Context.Request.RawUrl, mApp.Context.Request.UrlReferrer, mApp.Server.GetLastError().InnerException); else error = "No Context available -> " + mApp.Context.Server.GetLastError().InnerException; Log.Add(LogTypes.Error, User.GetUser(0), -1, error); Trace.TraceError(error); lock (unhandledErrors) { if (unhandledErrors.Count > 0) Trace.TraceError("New errors occurred while an error was being handled. The error handler Application_Error possibly raised another error, but was protected against an infinite loop."); foreach (Exception unhandledError in unhandledErrors) Trace.TraceError(unhandledError.StackTrace); } } } finally { // clear unhandled errors lock (unhandledErrors) { unhandledErrors.Clear(); } // flag we're done with the error handling handlingError = false; } } } #region IHttpModule Members /// ///Initializes a module and prepares it to handle requests. /// /// ///An that provides access to the methods, properties, and events common to all application objects within an ASP.NET application public void Init(HttpApplication context) { InitializeComponent(); ApplicationStart(context); context.BeginRequest += new EventHandler(Application_BeginRequest); context.AuthorizeRequest += new EventHandler(Application_AuthorizeRequest); context.PostAuthorizeRequest += new EventHandler(Application_PostAuthorizeRequest); context.PreRequestHandlerExecute += new EventHandler(Application_PreRequestHandlerExecute); context.Error += new EventHandler(Application_Error); mApp = context; } private void InitializeComponent() { components = new Container(); } /// ///Disposes of the resources (other than memory) used by the module that implements . /// /// public void Dispose() { } //this makes sure that times and other stuff is started on the first request, instead of depending on // application_start, which was inteded for being a httpContext-agnostic state. private static bool s_InitializedAlready = false; private static Object s_lock = new Object(); // Initialize only on the first request public void InitializeApplication(HttpApplication HttpApp) { if (s_InitializedAlready) return; lock (s_lock) { if (s_InitializedAlready) return; // Perform first-request initialization here ... try { Log.Add(LogTypes.System, User.GetUser(0), -1, "Application started at " + DateTime.Now); } catch { } // Check for configured key, checking for currentversion to ensure that a request with // no httpcontext don't set the whole app in configure mode if (GlobalSettings.CurrentVersion != null && !GlobalSettings.Configured) { HttpApp.Application["umbracoNeedConfiguration"] = true; } /* This section is needed on start-up because timer objects * might initialize before these are initialized without a traditional * request, and therefore lacks information on application paths */ /* Initialize SECTION END */ // add current default url HttpApp.Application["umbracoUrl"] = string.Format("{0}:{1}{2}", HttpApp.Context.Request.ServerVariables["SERVER_NAME"], HttpApp.Context.Request.ServerVariables["SERVER_PORT"], GlobalSettings.Path); // Start ping / keepalive timer pingTimer = new Timer(new TimerCallback(keepAliveService.PingUmbraco), HttpApp.Context, 60000, 300000); // Start publishingservice publishingTimer = new Timer(new TimerCallback(publishingService.CheckPublishing), HttpApp.Context, 600000, 60000); //Find Applications and event handlers and hook-up the events //BusinessLogic.Application.RegisterIApplications(); //define the base settings for the dependency loader to use the global path settings if (!CompositeDependencyHandler.HandlerFileName.StartsWith(GlobalSettings.Path)) CompositeDependencyHandler.HandlerFileName = GlobalSettings.Path + "/" + CompositeDependencyHandler.HandlerFileName; // init done... s_InitializedAlready = true; } } #endregion } }