public override VirtualFile GetFile(string virtualPath) { var currentPortalContext = PortalContext.Current; // office protocol: instruct microsoft office to open the document without further webdav requests when simply downloading the file // webdav requests would cause an authentication window to pop up when downloading a docx if (HttpContext.Current != null && HttpContext.Current.Response != null) { if (Repository.DownloadExtensions.Any(extension => virtualPath.EndsWith(extension))) { //we need to do it this way to support a 'download' query parameter with or without a value var queryParams = HttpContext.Current.Request.QueryString.GetValues(null); var download = HttpContext.Current.Request.QueryString.AllKeys.Contains("download") || (queryParams != null && queryParams.Contains("download")); if (download) { var fName = string.Empty; if (currentPortalContext != null && currentPortalContext.IsRequestedResourceExistInRepository) { // look for a content var node = Node.LoadNode(virtualPath); if (node != null) { fName = DocumentBinaryProvider.Current.GetFileName(node); } } else { // look for a file in the file system fName = Path.GetFileName(virtualPath); } HttpHeaderTools.SetContentDispositionHeader(fName); } } } if (DiskFSSupportMode == DiskFSSupportMode.Prefer && base.FileExists(virtualPath)) { var result = base.GetFile(virtualPath); //let the client code log file downloads if (PortalContext.Current != null && PortalContext.Current.ContextNodePath != null && string.Compare(virtualPath, PortalContext.Current.ContextNodePath, StringComparison.Ordinal) == 0) { File.Downloaded(virtualPath); } return(result); } // Indicates that the VirtualFile is requested by a HttpRequest (a Page.LoadControl also can be a caller, or an aspx for its codebehind file...) var isRequestedByHttpRequest = (HttpContext.Current != null) && (string.Compare(virtualPath, HttpContext.Current.Request.Url.LocalPath, StringComparison.InvariantCultureIgnoreCase) == 0); if (isRequestedByHttpRequest && currentPortalContext.IsRequestedResourceExistInRepository) { return(new RepositoryFile(virtualPath, currentPortalContext.RepositoryPath)); } else if (IsFileExistsInRepository(virtualPath)) //OPT: nem kéne még egyszer megnézni, hogy bent van-e... { return(new RepositoryFile(virtualPath, virtualPath)); } else if (IsFileExistsInAssembly(virtualPath)) { return(new EmbeddedFile(virtualPath)); } else { // Otherwise it may exist in the filesystem - call the base return(base.GetFile(virtualPath)); } }
private void HandleResponseForClientCache(PortalContextInitInfo initInfo) { // binaryhandler if (initInfo.BinaryHandlerRequestedNodeHead != null) { var bhMaxAge = Settings.GetValue(PortalSettings.SETTINGSNAME, PortalSettings.SETTINGS_BINARYHANDLER_MAXAGE, initInfo.RepositoryPath, 0); if (bhMaxAge > 0) { HttpHeaderTools.SetCacheControlHeaders(bhMaxAge); // We're only handling these if the visitor has permissions to the node if (CheckVisitorPermissions(initInfo.RequestedNodeHead)) { // handle If-Modified-Since and Last-Modified headers HttpHeaderTools.EndResponseForClientCache(initInfo.BinaryHandlerRequestedNodeHead.ModificationDate); } else { //otherwise store the value for later use initInfo.ModificationDateForClient = initInfo.BinaryHandlerRequestedNodeHead.ModificationDate; } return; } } if (initInfo.IsWebdavRequest || initInfo.IsOfficeProtocolRequest) { //HttpHeaderTools.SetCacheControlHeaders(0, HttpCacheability.NoCache); //HttpContext.Current.Response.Headers.Add("Cache-Control", "no-store, no-cache, private"); HttpContext.Current.Response.Headers.Add("Pragma", "no-cache"); //HTTP 1.0 HttpContext.Current.Response.Headers.Add("Expires", "Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past return; } // get requested nodehead if (initInfo.RequestedNodeHead == null) { return; } // if action name is given, do not do shortcircuit (eg. myscript.js?action=Edit should be a server-rendered page) if (!string.IsNullOrEmpty(initInfo.ActionName)) { return; } // ********************************************************** // Image content check is moved to OnAuthorize event handler, because it needs the // fully loaded node. Here we handle only other content - e.g. js/css files. // ********************************************************** if (!initInfo.RequestedNodeHead.GetNodeType().IsInstaceOfOrDerivedFrom(typeof(Image).Name)) { var cacheSetting = GetCacheHeaderSetting(initInfo.RequestUri, initInfo.RequestedNodeHead); if (cacheSetting.HasValue) { HttpHeaderTools.SetCacheControlHeaders(cacheSetting.Value); // We're only handling these if the visitor has permissions to the node if (CheckVisitorPermissions(initInfo.RequestedNodeHead)) { // handle If-Modified-Since and Last-Modified headers HttpHeaderTools.EndResponseForClientCache(initInfo.RequestedNodeHead.ModificationDate); } else { //otherwise store the value for later use initInfo.ModificationDateForClient = initInfo.RequestedNodeHead.ModificationDate; } return; } } // applications Application app; // elevate to sysadmin, as we are startupuser here, and group 'everyone' should have permissions to application without elevation using (new SystemAccount()) { //load the application, or the node itself if it is an application if (initInfo.RequestedNodeHead.GetNodeType().IsInstaceOfOrDerivedFrom("Application")) { app = Node.LoadNode(initInfo.RequestedNodeHead) as Application; } else { app = ApplicationStorage.Instance.GetApplication(initInfo.ActionName, initInfo.RequestedNodeHead, initInfo.DeviceName); } } if (app == null) { return; } var maxAge = app.NumericMaxAge; var cacheControl = app.CacheControlEnumValue; if (cacheControl.HasValue && maxAge.HasValue) { HttpHeaderTools.SetCacheControlHeaders(maxAge.Value, cacheControl.Value); // We're only handling these if the visitor has permissions to the node if (CheckVisitorPermissions(initInfo.RequestedNodeHead)) { // handle If-Modified-Since and Last-Modified headers HttpHeaderTools.EndResponseForClientCache(initInfo.RequestedNodeHead.ModificationDate); } else { //otherwise store the value for later use initInfo.ModificationDateForClient = initInfo.RequestedNodeHead.ModificationDate; } } }
public override Stream Open() { if (_node == null) { try { var allowCompiledContent = AllowCompiledContent(_repositoryPath); // http://localhost/TestDoc.docx?action=RestoreVersion&version=2.0A // When there are 'action' and 'version' parameters in the requested URL the portal is trying to load the desired version of the node of the requested action. // This leads to an exception when the action doesn't have that version. // _repositoryPath will point to the node of action and ContextNode will be the document // if paths are not equal then we will return the last version of the requested action. // We also have to ignore the version request parameter in case of binary handler, because that // ashx must not have multiple versions. if (PortalContext.Current == null || PortalContext.Current.BinaryHandlerRequestedNodeHead != null || string.IsNullOrEmpty(PortalContext.Current.VersionRequest) || _repositoryPath != PortalContext.Current.ContextNodePath) { if (allowCompiledContent) { // elevated mode: pages, ascx files, etc. using (new SystemAccount()) { _node = Node.LoadNode(_repositoryPath, VersionNumber.LastFinalized); } } else { _node = Node.LoadNode(_repositoryPath, VersionNumber.LastFinalized); } } else { VersionNumber version; if (VersionNumber.TryParse(PortalContext.Current.VersionRequest, out version)) { Node node; if (allowCompiledContent) { // elevated mode: pages, ascx files, etc. using (new SystemAccount()) { node = Node.LoadNode(_repositoryPath, version); } } else { node = Node.LoadNode(_repositoryPath, version); } if (node != null && node.SavingState == ContentSavingState.Finalized) { _node = node; } } } // we cannot serve the binary if the user has only See or Preview permissions for the content if (_node != null && (_node.IsHeadOnly || _node.IsPreviewOnly) && HttpContext.Current != null) { AuthenticationHelper.ThrowForbidden(_node.Name); } } catch (SecurityException ex) //logged { Logger.WriteException(ex); if (HttpContext.Current == null || (_repositoryPath != null && _repositoryPath.ToLower().EndsWith(".ascx"))) { throw; } AuthenticationHelper.DenyAccess(HttpContext.Current.ApplicationInstance); } } if (_node == null) { throw new ApplicationException(string.Format("{0} not found. RepositoryFile cannot be served.", _repositoryPath)); } string propertyName = string.Empty; if (PortalContext.Current != null) { propertyName = PortalContext.Current.QueryStringNodePropertyName; } if (string.IsNullOrEmpty(propertyName)) { propertyName = PortalContext.DefaultNodePropertyName; } var propType = _node.PropertyTypes[propertyName]; if (propType == null) { throw new ApplicationException("Property not found: " + propertyName); } ////<only_for_test> //if(propType == null) //{ // StringBuilder sb = new StringBuilder(); // sb.AppendFormat("Property {0} not found.", propertyName); // sb.AppendLine(); // foreach (var pt in _node.PropertyTypes) // { // sb.AppendFormat("PropertyName='{0}' - DataType='{1}'", pt.Name, pt.DataType); // sb.AppendLine(); // } // return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(sb.ToString())); //} ////</only_for_test> var propertyDataType = propType.DataType; Stream stream; switch (propertyDataType) { case DataType.Binary: string contentType; BinaryFileName fileName; stream = DocumentBinaryProvider.Current.GetStream(_node, propertyName, out contentType, out fileName); if (stream == null) { throw new ApplicationException(string.Format("BinaryProperty.Value.GetStream() returned null. RepositoryPath={0}, OriginalUri={1}, AppDomainFriendlyName={2} ", this._repositoryPath, ((PortalContext.Current != null) ? PortalContext.Current.OriginalUri.ToString() : "PortalContext.Current is null"), AppDomain.CurrentDomain.FriendlyName)); } //Set MIME type only if this is the main content (skip asp controls and pages, //page templates and other repository files opened during the request). //We need this in case of special images, fonts, etc, and handle the variable //at the end of the request (PortalContextModule.EndRequest method). if (HttpContext.Current != null && PortalContext.Current != null && PortalContext.Current.RepositoryPath == _repositoryPath && (string.IsNullOrEmpty(PortalContext.Current.ActionName) || PortalContext.Current.ActionName == "Browse") && !string.IsNullOrEmpty(contentType) && contentType != "text/asp") { if (!HttpContext.Current.Items.Contains(RESPONSECONTENTTYPEKEY)) { HttpContext.Current.Items.Add(RESPONSECONTENTTYPEKEY, contentType); } //set the value anyway as it may be useful in case of our custom file handler (StaticFileHandler) HttpContext.Current.Response.ContentType = contentType; //add the necessary header for the css font-face rule if (MimeTable.IsFontType(fileName.Extension)) { HttpHeaderTools.SetAccessControlHeaders(); } } //set compressed encoding if necessary if (HttpContext.Current != null && MimeTable.IsCompressedType(fileName.Extension)) { HttpContext.Current.Response.Headers.Add("Content-Encoding", "gzip"); } //let the client code log file downloads var file = _node as ContentRepository.File; if (file != null) { ContentRepository.File.Downloaded(file.Id); } break; case DataType.String: case DataType.Text: case DataType.Int: case DataType.DateTime: stream = new MemoryStream(Encoding.UTF8.GetBytes(_node[propertyName].ToString())); break; default: throw new NotSupportedException(string.Format("The {0} property cannot be served because that's datatype is {1}.", propertyName, propertyDataType)); } return(stream); }
void OnAuthorize(object sender, EventArgs e) { //At this point the user has at least some permissions for the requested content. This means //we can respond with a 304 status if the content has not changed, without opening a security hole. //This value could be set earlier by the HandleResponseForClientCache method //(e.g. because of binaryhandler or application client cache values). if (PortalContext.Current.ModificationDateForClient.HasValue) { HttpHeaderTools.EndResponseForClientCache(PortalContext.Current.ModificationDateForClient.Value); } //check requested nodehead if (PortalContext.Current.ContextNodeHead == null) { return; } // Check if the requested content is executable (e.g. an aspx file) and has the correct file type. We must // call this here instead of OnEnter because the user must be authenticated for the check algorithm to work. CheckExecutableType(PortalContext.Current.ContextNodeHead, PortalContext.Current.ActionName); var modificationDate = PortalContext.Current.ContextNodeHead.ModificationDate; //If action name is given, do not do shortcircuit (eg. myimage.jpg?action=Edit //should be a server-rendered page) - except if this is an image resizer application. if (!string.IsNullOrEmpty(PortalContext.Current.ActionName)) { var remapAction = PortalContext.Current.CurrentAction as RemapHttpAction; if (remapAction == null || remapAction.HttpHandlerNode == null) { return; } if (!remapAction.HttpHandlerNode.GetNodeType().IsInstaceOfOrDerivedFrom(typeof(ImgResizeApplication).Name)) { return; } //check if the image resizer app was modified since the last request if (remapAction.HttpHandlerNode.ModificationDate > modificationDate) { modificationDate = remapAction.HttpHandlerNode.ModificationDate; } } //set cache values for images, js/css files var cacheSetting = GetCacheHeaderSetting(PortalContext.Current.RequestedUri, PortalContext.Current.ContextNodeHead); if (cacheSetting.HasValue) { HttpHeaderTools.SetCacheControlHeaders(cacheSetting.Value); //in case of preview images do NOT return 304, because _undetectable_ permission changes //(on the image or on one of its parents) may change the preview image (e.g. display redaction or not). if (DocumentPreviewProvider.Current == null || !DocumentPreviewProvider.Current.IsPreviewOrThumbnailImage(PortalContext.Current.ContextNodeHead)) { //end response, if the content has not changed since the value posted by the client HttpHeaderTools.EndResponseForClientCache(modificationDate); } } }