Setting multiple IIS Custom HTTP error messages on InstallShield 2009 - iis-6

How do I set the HTTP custom error messages on a website installed by InstallShield 2009?
It does look deceptively simple. There is an Internet Information Services view, where we can create website or virtual directory objects, and one of the tab for those objects looks like it may let us set custom HTTP Error messages.
It even works to reset 1 or 2 error message, but when I try to reset all the HTTP error message, it does fail.
Specifically, this is the ISIISCommon table on my InstallShield project (XML file format):
<table name="ISIISCommon">
<col key="yes" def="s72">ISIISCommon</col>
<col def="S72">ISIISCommon_Parent</col>
<col def="L255">DisplayName</col>
<col def="s50">RootDir</col>
<col def="i4">Attributes</col>
<col def="L255">DefDoc</col>
<col def="I4">SessionTimeout</col>
<col def="I4">ScriptTimeout</col>
<col def="S255">AnonyUserName</col>
<col def="S255">AnonyPasswrd</col>
<col def="S0">CustomErrors</col>
<col def="L255">AppName</col>
<col def="S72">SSLCert</col>
<col def="L72">AppPool_</col>
<col def="S255">Aspversion</col>
<col def="S255">SslPassword</col>
<col def="S255">HostHeaderName</col>
<row><td>ISIISCommonVRoot</td><td>ISIISCommonWebsite1</td><td>##ID_STRING121##</td><td>WEB</td><td>25873</td><td>Index.html</td><td>20</td><td>90</td><td/><td/><td>400,*,URL,CustomError.html;401,1,URL,CustomError.html;401,2,URL,CustomError.html;401,3,URL,CustomError.html;401,4,URL,CustomError.html;401,5,URL,CustomError.html;403,1,URL,CustomError.html;403,10,URL,CustomError.html;403,11,URL,CustomError.html;403,12,URL,CustomError.html;403,13,URL,CustomError.html;403,14,URL,CustomError.html;403,15,URL,CustomError.html;403,16,URL,CustomError.html;403,17,URL,CustomError.html;403,2,URL,CustomError.html;403,3,URL,CustomError.html;403,4,URL,CustomError.html;403,5,URL,CustomError.html;403,6,URL,CustomError.html;403,7,URL,CustomError.html;403,8,URL,CustomError.html;403,9,URL,CustomError.html;404,*,URL,CustomError.html;405,*,URL,CustomError.html;406,*,URL,CustomError.html;407,*,URL,CustomError.html;412,*,URL,CustomError.html;414,*,URL,CustomError.html;500,*,URL,CustomError.html;500,100,URL,CustomError.html;500,12,URL,CustomError.html;500,13,URL,CustomError.html;500,15,URL,CustomError.html;501,*,URL,CustomError.html;502,*,URL,CustomError.html</td><td>##ID_STRING127##</td><td/><td>##ID_STRING31##</td><td/><td/><td/></row>
</table>
As you may notice, I tried to set all of the HTTP errors on my virtual directory to be redirected to a CustomError.html page. Note also that I think storing this information inside a TD element, and not in its own TABLE, is faulty to a fault, especially on the InstallShield/Windows Installer world.
This doesn't work, on various levels.
First and foremost, here is the installation log,
MSI (s) (60:14) [12:28:49:025]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI72A0.tmp, Entrypoint: CreateIISVRoots
InstallShield 12:28:49: User legacy object preference is the following: 1 . This is based on the value of the following property: IISPREFERLEGACYOBJECTS
InstallShield 12:28:49: InitIISObject
InstallShield 12:28:49: CreateAppPoolFunction
InstallShield 12:28:49: OpenKeyLog, pszMDPath =/LM
InstallShield 12:28:49: AddKey, pszMDPath = /W3SVC/AppPools/MyAppPool
InstallShield 12:28:49: OpenKeyLog, pszMDPath =/LM/W3SVC/AppPools/MyAppPool
InstallShield 12:28:49: getvaluelog
InstallShield 12:28:49: SetVRtStrProperty for property '1002' with value 'IIsApplicationPool' and strSubPath '/'.
[zip]
InstallShield 12:28:49: SetVRtStrProperty for property '6006' with value 'Index.html' and strSubPath '/'.
InstallShield 12:28:49: SetData, pszMDPath = /
InstallShield 12:28:49: SetVRtDelimMultiszProperty for property '6008' with value '400,*,URL,CustomError.html;401,1,URL,CustomError.html;401,2,URL,CustomError.html;401,3,URL,CustomError.html;401,4,URL,CustomError.html;401,5,URL,CustomError.html;403,1,URL,CustomError.html;403,10,URL,CustomError.html;403,11,URL,CustomError.html;403,12,URL,Cus'.
**InstallShield 12:28:49: SetVRtMultiszProperty for property '6008' and value '400,*,URL,CustomError.html' with length '522'**.
By the look of it, it doesn't like that 522 characters are being used to describe the custom errors. In IIS, only the first 259 characters were consumed, so only an handful of custom errors were correctly set up, the last of one grossly incorrectly (the resulting custom error URL for 403.12 being "cus").
Is there any way to work around this limitation?

I renamed the CustomError page from CustomError.html to ce.htm. This saved some space, but was not enough. A bigger char buffer saver was the good old *. Instead of
401,1,URL,CustomError.html;401,2,URL,CustomError.html;...;401,13,CustomError.html;401,2,URL,CustomError.html
I end up using:
401,*,FILE,ce.htm;
And this how the virtual directory row looks now in the ISIISCommon table:
<row><td>ISIISCommonVRoot</td><td>ISIISCommonWebsite1</td><td>##ID_STRING121##</td><td>WEB</td><td>25873</td><td>Index.html</td><td>20</td><td>90</td><td/><td/><td>400,*,FILE,ce.htm;401,*,FILE,ce.htm;403,*,FILE,ce.htm;404,*,FILE,ce.htm;405,*,FILE,ce.htm;406,*,FILE,ce.htm;407,*,FILE,ce.htm;412,*,FILE,ce.htm;414,*,FILE,ce.htm;500,*,FILE,ce.htm;501,*,FILE,ce.htm;502,*,FILE,ce.htm</td><td>##ID_STRING127##</td><td/><td>##ID_STRING31##</td><td/><td/><td/></row>
This may not be suitable for all scenarios (what if someone would like to have different customer errors for 401.1 and 401.2?).
Also, I had to make this change directly on the InstallShield XML project file, as the GUI wouldn't understand that (and still does, even do it load up the project, and build it correctly, the Custom Errors tab is a mess, I just discount the DEFAULT or suberror specialized entries, they are not there in the resulting MSI file, or this is what Orca says anyway).
I also moved from URL to FILE as the latter seems more orthogonal to authentication.
It would be great to read a better solution, and I still wonder why in such a world of tables, someone thought it was going to be a good idea to store a table like that.

Related

Why is MsiGetFeatureState() returning INSTALLSTATE_ADVERTISED for my un-advertisible feature?

I've got an MSI built in WiX defined as below:
<Feature Id="Core"
Display="0"
Absent="disallow"
ConfigurableDirectory="INSTALLDIR"
AllowAdvertise="no"
Level="1">...</Feature>
I have a 'commit' custom action that loops through all the features of the MSI and determines their install-state. The log file shows this "Core" feature as installed 'Local', but MsiGetFeatureState returns INSTALLSTATE_ADVERTISED. I thought that was impossible given I set:
AllowAdvertise="no"
FWIW, MsiGetFeatureState correctly returns INSTALLSTATE_LOCAL for all other installed features and INSTALLSTATE_ABSENT for all other not-installed features.
Edit for more info:
This occurs during a fresh install.
I do not set the ADVERTISE property (I still don't fully understand what it's for)
The "Core" feature is a parent feature with children that all have the wix attribute InstallDefault="followParent".
The "Core" feature (as well as its children) all have components attached to them.
All the child features are marked as INSTALLSTATE_ADVERTISED as well.
I have a commit custom action (scheduled before InstallFinalize) that queries the installed feature states ([ProductCode] is passed in via CustomActionData). I assumed that a commit action was the right choice since the MSI is officially installed by that point.
AllowAdvertise="no" turns into msidbFeatureAttributesDisallowAdvertise in the Feature table, which says:
Note that this bit works only with features that are listed by the ADVERTISE Property.
IOW, if it became advertised for some other reason, this bit won't stop that.
Found out what the problem was (I think).
I had a custom action that would call MsiSetComponentState() to set a component to INSTALLSTATE_ABSENT if some condition was true. This means that my 'Core' feature was intended to have every attached component set to INSTALLSTATE_LOCAL, but since this one component was manually forced into INSTALLSTATE_ABSENT the 'Core' feature (once installed) is considered to have an install state of 'Advertised'. An additional consequence was that during uninstall all components attached to the 'Core' feature were getting left behind. Their action states were NULL (do nothing) instead of Absent (remove).
Moral of the story, don't use MsiSetComponentState() to turn off a component at install time.

Again "A Web Part or Web Form control on this Page cannot be displayed or imported. " in editorPart project

I have a question concerning safe controls in Sharepoint development. I went through several proposed solutions for this problem but neither of them were able to fix the issue. What I basically did was to translate a C# project into VB one using Visual Studio 2010.
This project creates a Visual Web Part in Sharepoint and overrides a set of procedures/functions so you can get a dropdown list populated with SPP lists in the web editor part custom properties. The new VB projects seems free of bugs or translation errors because when I build the project I don't receive any warning/error. The solution deployment is correct, but when I try to use my web part in an empty spp page I receive the infamous message "A Web Part or Web Form control on this Page cannot be displayed or imported. The type could not be found or it is not registered as safe".
What I double-checked is the project visual web part "safe control entries" collection that contains the (supposed) correct entry with a valid Strong Name
I did several modification to this part of project; i.e., in the IIS web.config there is still an entry as <safecontrols> containing the same information found in "Safe control entries". I even created another empty project leaving all default names trying to avoid this kind of problem when you rename the Visual Web Part name but still with no luck.
Update
Hi #Tome. If I can, I'll be more than happy to attach the source project.
#Adi, I cannot debug the application because my local Sharepoint refuse to use the web part because of the unsafe control.
For the chronicle, my SPP 2010 runs on top of a W2008 server in a virtual machine and I trust it works because this is not the first project I wrote. As I said I'm quite new in SPP developing but I know how to find the information I need in order to go over the issues I encounter while programming in a new environment (not this time, it seems).
The original C# project runs flawlessy but, evidently, I wrote some wrong lines during translation in VB. As you can see below, the project's file SharePointProjectItem.spdata contains the line tagged <SafeControl>.
<?xml version="1.0" encoding="utf-8"?>
<ProjectItem Type="Microsoft.VisualStudio.SharePoint.VisualWebPart" DefaultFile="LeaveWebPartUserControl.ascx" SupportedTrustLevels="FullTrust" SupportedDeploymentScopes="Site" xmlns="http://schemas.microsoft.com/VisualStudio/2010/SharePointTools/SharePointProjectItemModel">
<Files>
<ProjectItemFile Source="Elements.xml" Target="LeaveWebPart\" Type="ElementManifest" />
<ProjectItemFile Source="LeaveWebPart.webpart" Target="LeaveWebPart\" Type="ElementFile" />
<ProjectItemFile Source="LeaveWebPartUserControl.ascx" Target="CONTROLTEMPLATES\LeaveApplicationVB\LeaveWebPart\" Type="TemplateFile" />
</Files>
<SafeControls>
<SafeControl Name="SafeControlEntry1" Assembly="$SharePoint.Project.AssemblyFullName$" Namespace="LeaveApplicationVB.LeaveWebPart" TypeName="*" IsSafe="true" IsSafeAgainstScript="false" />
</SafeControls>
</ProjectItem>
By the way, I'd give a try again this morning, rewriting every thing from scratch and see what I can get.
The issue raised by SPP concerning "unsafe control" was because, during C# project translation, I did include namespace identifiers as per C# syntax.
Removing namespace identifiers from VB code did solve the problem.
Now the project is able to create a custom editor part for my visual web part.

Override forcedownload behavior in Sitecore

We had a problem with some of our IE clients failing to download a PDF, even after clicking on the link. We found the answer here resolved our problems: set forcedownload=true for PDF mime types in web.config.
However, that created another problem: we are now unable to render a PDF in a browser when we want to. We used to do this with an iframe. However, as you can see, the PDF just downloads, and does not render in the browser.
I learned that the forcedownload=true setting is actually a default in a subsequent version of Sitecore (v7.2). So, I'm hesitant to revert that.
So, how do I render a PDF in a browser in this situation?
You can leave forceDownload=false on the PDF mime type and instead set the following setting to false:
<setting name="Media.EnableRangeRetrievalRequest" value="false"/>
I faced the same dilema a few months back with the same initial fix. Found out the actual issue last week, I wrote a blog post about it. (In fact, I wrote the answer you linked to, I've updated it with the same information now for future visitors)
The issue is basically a combination of Adobe Reader plugin for IE9, chunked transfer encoding and streaming the file directly from the database. I found if you close your browser and try again, or force refresh with Ctrl+F5 it worked fine. Once Sitecore had cached the file to disk it would continue to work for everyone.
The above setting disables chunked transfer encoding, instead sending the file down to the browser as a single piece. This setting was introduced in Sitecore 6.5+
This is one of the flaws in the MediaRequestHandler and in my opinion; the forceDownload option is pretty useless the way it is designed by default. (Why would ever want to configure this option on media extension only?)
You’ll have to basically turn off the forcedownload option again and replace the MediaRequestHandler with your own one. I usually end up with writing my own anyway because if other issues with the default handler, such as dealing properly with CDN’s etc.
In the ProcessRequest pipeline, you can determine if the item should be “downloaded” or not by setting the Content-Disposition header. You basically need to get rid of the default handling of forceDownload and set your headers based on your own logic.
Personally I prefer to set a query string parameter, such as ?dl=1, and base the Content-Disposition header on this. You could also extend the MediaItem template to contain a default behavior on each item or sub tree (leverage from Sitecore inheritance and standard values), and potentially you could thereby also define (override) a specific filename on each item for the attachment part in the Content-Disposition header.
When rendering the link, you can leverage from the properties collection (write a suitable extension method or similar), so that you can clearly mark your code that the link is meant for download, but still leverage from the built in field render methods. Thereby you eliminate the risk of messing up the page editor etc.
/ Mikael
You have to disable range retrieval request in web.config by setting its value to false.
<setting name="Media.EnableRangeRetrievalRequest" value="false" />
MediaRequestHandler enables Sitecore to download PDF content partially in range using HTTP 206 Status code. You can also overwrite MediaRequestHandler and write your own custom implementation to handle media request.

Ignoring disallowed property IISMAJORVERSION and 'EnableUserControl'?

My installer package works on my machine but failed on my colleague's machine. Looking at the log, I can see a few properties values are not persisted during ExecuteAction phrase: there are few Ignoring disallowed property lines in his installer log file and those properties are set with the right value during UISequence. The strange thing is that we are both Admin user on local machines he is and Domain Admin as well while I am not, and we are both on Windows 7 64bit. So I don't think it is because he don't have Administrator rights.
The exact entries in the log file:
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property IISMAJORVERSION
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property IISMINORVERSION
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property WCF_SRV_INSTALL_FOLDER
MSI (s) (3C:50) [09:14:16:583]: Ignoring disallowed property TARGETDIR
A easy fix would be mark all those properties as Secure, but properties such as IISMAJORVERSION are not defined in my code. I discovered that I can set EnableUserControl to 1 to make Ignoring disallowed property go away. Another solution would be create my own security properties and copy whatever ignored property value I need into my own properties and read them instead of the ignored property.
After that I discovered there is an entry Machine policy value 'EnableUserControl' is 0 in his installer log, but not in my log, which seems point to set EnableUserControl to 1 is probably what I need. In that case, the question is why there are different values from those two machines?
So my number one question would be: is set EnableUserControl a good fix for my solution, or there are probably better/safer solutions given I only seems found the symptoms but not the cause?
Or if EnableUserControl seems like a reasonable fix given the information, any suggestion could help me track down the cause of the problem (a registry key value change by the Administrator, probably?).
I don't think there is anything special about my installer, but in case of anyone want to see more details:
<Package Id="*" InstallerVersion="200" Description="Web service installer" Compressed="yes" InstallScope="perMachine" />
Edit:
As pointed out by PhilDW, those properties probably should be marked as Secure to begin with. But then shouldn't all properties be marked as Secure because of UAC, I don't think is make much sense to define a property to be not Secure if it might be used by others?
I'm pretty sure it doesn't matter if you defined them or not - try marking them Secure.

Prevent custom actions from running during patch

I am authoring a very small patch to a very large package, it's sole purpose is to update a single file and add four smaller ones.
Using the WiX help as a guide I am able to generate the MSP file.
However, a patch runs apparently it runs the original package in reinstall mode, with all the custom actions and whatnot that go along with it, which is not what I want.
Further research turned up the OptimizeCA property of the MsiPatchMetadata table, and its WiX equivalent OptimizeCustomActions which allows custom actions to be skipped when applying a patch.
That sounded exactly what I wanted but unfortunately it did not work as expected. The original package has a bunch of XML config file change custom actions, and looking at the log it appears to be erroring out when it hits the XmlFile CAs:
MSI (s) (58!74) [14:53:24:928]: PROPERTY CHANGE: Adding ExecXmlFileRollback property. Its value is (stuff deleted)
MSI (s) (58:74) [14:53:24:928]: Doing action: ExecXmlFileRollback
Action 14:53:24: ExecXmlFileRollback.
Action start 14:53:24: ExecXmlFileRollback.
MSI (s) (58:74) [14:53:24:928]: Skipping Action: ExecXmlFileRollback. It is being skipped as per the value provided for OptimizeCA in MsiPatchMetadata table of an applicable patch
Action ended 14:53:24: ExecXmlFileRollback. Return value 0.
SchedXmlFile: Error 0x8007065a: Failed MsiDoAction on deferred action
SchedXmlFile: Error 0x8007065a: failed to schedule ExecXmlFileRollback for file: (file)
SchedXmlFile: Error 0x8007065a: failed to begin file change for file: (file)
Action ended 14:53:24: SchedXmlFile. Return value 3.
You can see that the OptimizeCA property is being respected for the ExecXmlFileRollback action, as it is being skipped, but something goes wrong when it tries to schedule it and that causes the whole thing to crash the installer.
This is the relevant part of my WiX patch authoring:
<OptimizeCustomActions
SkipAssignment="no"
SkipImmediate="no"
SkipDeferred="yes">
</OptimizeCustomActions>
I'm really having a hard time figuring out what to try next. I found another person having the exact same issue as me on wix-users but there was no response to the query.
A condition including "Not PATCH" should prevent the CA from running during patch install.
Alternatively, "Not Installed" would prevent it from running if the product is already installed, because it looks like you'd have a similar issue during a repair.