mikeobrien.net Curriculum Vitae Blog Labs
Wednesday, March 03, 2010

We ran into a situation where we needed to resolve a mime type for a file extension in a RESTful service running under IIS6. We didn’t want to build a mapping ourselves somewhere but wanted to use a mapping that already existed. Here is a class that will do the mapping, using IIS as the source. You will need to add a reference to System.DirectoryServices and a reference to the “Active DS IIS Namespace Provider” COM component.

public static class MimeTypes
{
    private static Dictionary<string, string> _mimeTypes;

    public static string ResolveMimeTypeFromExtension(string extension)
    {
        LoadMimeTypeMapping();
        if (!extension.StartsWith(".")) extension = "." + extension;
        return _mimeTypes.ContainsKey(extension) ? _mimeTypes[extension] : null;
    }

    public static string ResolveExtensionFromMimeType(string mimeType)
    {
        LoadMimeTypeMapping();
        return _mimeTypes.ContainsValue(mimeType) ? 
            _mimeTypes.First(mapping => mapping.Value == mimeType).Key : null;
    }

    private static void LoadMimeTypeMapping()
    {
        if (_mimeTypes != null) return;

        _mimeTypes = new Dictionary<string, string>();

        DirectoryEntry path = new DirectoryEntry("IIS://localhost/MimeMap");
        PropertyValueCollection mimeMap = path.Properties["MimeMap"];

        foreach (var mapping in mimeMap.OfType<IISOle.IISMimeType>())
            _mimeTypes.Add(mapping.Extension, mapping.MimeType);            
    }
}
.NET | IIS 6
Wednesday, March 03, 2010 8:25:26 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Monday, March 30, 2009

I had a weird problem on our CI box (IIS6) where the WSDL was generated via HTTP GET but the associated XML schema files were not (IE: https://ci.yada.net/System.svc?xsd=xsd0). When you browsed to the link the browser would return a "general" HTTP error. Erwyn Van Der Meer saved the day with his blog post on how to solve this. Basically the app pool identity needs to have "list folder/read data" permissions on the %SYSTEM%\Temp folder.

IIS 6 | WCF
Monday, March 30, 2009 10:38:04 PM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Tuesday, March 10, 2009

I keep forgetting how to do this, here it is:

cscript C:\Inetpub\AdminScripts\adsutil.vbs set /w3svc/<SiteId>/SecureBindings ":443:<HostHeader>"

If you wanna know how to do it with IIS7 look here.

Tuesday, March 10, 2009 1:11:57 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Wednesday, October 22, 2008

I have been having XmlTraceListener woes... None of which are showstoppers just major time wasters! The following bug is fixed as of version 4.1! The first of which has to do the message. The message is not escaped and/or CDATA qualified. So if you have an ampersand or greater/less than sign in the message it will throw this cryptic error:

An error occurred while parsing EntityName. Line x, position y.

I ended up manually running the log entry object through the Format() method on the XmlLogFormatter class to get the raw xml and see what exactly it was complaining about. Sure enough there was an ampersand. Adding an extension method to the string class and manually escaping the LogEntry message alleviated this to some degree. Bad thing is though, the message is escaped for all listeners and you probably don't want to see HTML entities in your event log entry or email message. I think the only way around this would be to write your own xml trace listener.

namespace MyApp.Runtime.Extensions
{
    public static class String
    {
        public static string EscapeUnsafeXmlCharacters(this string value)
        {
            return value.Replace("<", "&lt;").Replace(">", "&gt;").Replace("&", "&amp;");
        }
    }
}

Issue #2 has to do with where the log file is saved. The FlatFileTraceListner actually saves the log file relative to the application root (Where the .config file is). Take a look at the RootFileNameAndEnsureTargetFolderExists() method in the FormattedTextWriterTraceListener class (The FlatFileTraceListener's base class). Before it passes the filename to it's base class constructor, it runs it through this method. The XmlTraceListener on the other hand does not follow this pattern which had me going nuts trying to figure out what I was doing wrong (Thinking that it just wasn't saving at all). I wasn't getting any errors from the XmlTraceListener (More on this below) so I started to get the feeling that perhaps it was writing to the log but just not where I expected. So I fired up Process Monitor and didn't see any log writes. Then I switched the app (It was a web application project BTW) to use the built in web server instead of IIS. Ran everything again and voilà:

image

So it was saving it somewhere else, but only when running under the built in web server. Looking at the code in reflector you can see that, unlike the FormattedTextWriterTraceListener, the XmlWriterTraceListener does not modify the path to be the application root before it passes it to it's base class constructor. So the directory ends up being that of the entry assembly. Bottom line is you have to supply an explicit path when you're using the XmlTraceListener in a web application.

This leads me to the third oddity; where it would save running under the builtin web server but not IIS. The base class, TextWriterTraceListener, calls the EnsureWriter() method (Shown below) before writing to the file. If it returns true it writes the entry, otherwise it doesn't. Notice that if there is a UnauthorizedAccessException it just returns false and then subsequently, in the calling Write method, silently skips writing. When I was using IIS as my web server (And the code was running as the Network Service account) I didn't see any exceptions and no log writes were showing up in Process Monitor. Which was very confusing! But when I switched over to using the built in web server (Which was obviously running as my interactive account) I saw the log writes. So when it was running under the Network Service account it obviously did not have permissions to save the log file to wherever it was trying to save it and no exception was raised... Very confusing behavior this. Reminds me of the advice in Jeffrey Richter's CLR Via C# book not to ever swallow exceptions! So keep that in mind, if you don't see any errors and log writes with the XmlTraceListner, it may be permissions and/or path related.

Well, hopefully this wasn't too long winded and hopefully it clears up some oddities with the XmlTraceListener.

public class TextWriterTraceListener : TraceListener
{
    public override void Write(string message)
    {
        if (this.EnsureWriter())
        {
            if (base.NeedIndent)
            {
                this.WriteIndent();
            }
            this.writer.Write(message);
        }
    }
    internal bool EnsureWriter()
    {
        bool flag = true;
        if (this.writer == null)
        {
            flag = false;
            if (this.fileName == null)
            {
                return flag;
            }
            Encoding encodingWithFallback = GetEncodingWithFallback(new UTF8Encoding(false));
            string fullPath = Path.GetFullPath(this.fileName);
            string directoryName = Path.GetDirectoryName(fullPath);
            string fileName = Path.GetFileName(fullPath);
            for (int i = 0; i < 2; i++)
            {
                try
                {
                    this.writer = new StreamWriter(fullPath, true, encodingWithFallback, 0x1000);
                    flag = true;
                    break;
                }
                catch (IOException)
                {
                    fileName = Guid.NewGuid().ToString() + fileName;
                    fullPath = Path.Combine(directoryName, fileName);
                }
                catch (UnauthorizedAccessException)
                {
                    break;
                }
                catch (Exception)
                {
                    break;
                }
            }
            if (!flag)
            {
                this.fileName = null;
            }
        }
        return flag;
    }

    // Other members removed for brevity...
}
Wednesday, October 22, 2008 4:54:38 AM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Friday, April 25, 2008

So I had FTP setup on a Server '03 box with an FTP site configured to use user isolation mode with active directory. Everything was working great then all of a sudden users cant login and this error appears in the System events in the event log:

image

This error, while true, doesn't really pinpoint the problem... But I found this wonderful article that suggested that the account used by the FTP site to access AD was changed. "Oh yeah!", I said, "I did change that, D'OH!". Easy enough to fix; just open a command prompt and use the adsutil.vbs to update the AD account in the IIS metabase.

c:\Inetpub\Adminscripts\adsutil.vbs set msftpsvc/{FTP_SITE_ID}/ADConnectionsUserName MyDomain\MyFTPADUsername

c:\Inetpub\Adminscripts\adsutil.vbs set msftpsvc/{FTP_SITE_ID}/ADConnectionsPassword S0m3P@$$W0rd 

Remember to include the domain name with the username.

Friday, April 25, 2008 7:37:51 PM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Wednesday, April 16, 2008

If you happen to be using the Persits mail component and you get the 'Access is denied' exception:

Persits.MailSender.4 error '800a0011'

Access is denied.

/pages/somepage.asp, line 208

You'll probably find, from the Persits support site or elsewhere, that the queue folder needs to have increased permissions. It doesn't however tell you where this is. So here is the default install path for version 4:

C:\Program Files\Persits Software\AspEmail\Queue

As far as the permissions go I have found that granting Read and Write permissions to the accessing account on that folder is sufficient. In IIS 6 the accessing account for .NET will be the identity of the App Pool assigned to the site (if you are not doing impersonation) which is the NetworkService account by default. For Classic ASP it will be the anon account (Which by default is the IUSR_xxx) for anon access or the user/group if you are doing other types of auth.

Wednesday, April 16, 2008 4:21:07 PM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Friday, June 22, 2007

Another SharePoint anomaly... Will they ever end??? So I have had this strange issue where doing a deploy of a VSeWSS project does not seem to update the SharePoint site for certain elements like schemas. I have to retract/remove the solution manually from SP, close/reopen Visual Studio (Otherwise I would get a null ref error), then redeploy to see changes. The VSeWSS deploy process does an iisreset (Which I tried manually doing as well) but the updates do not appear without following the steps mentioned. I checked the SP database and SP hive and the elements in question were deployed... So the only thing I could think of is that these elements must cached in memory, which was baffling since an iisreset should clear that out (Right?). Well I finally got the bright idea to verify that iisreset was in fact closing out the IIS service process completely. And lo an behold it was not! After further investigation I found that the “Windows Remote Management (WS-Management)” service, which depends on IIS, was preventing a complete exit of the inetinfo process. I'm not sure what the iisreset was actually doing all along (It said it successfully reset the IIS services), possibly just recycling the AppDomain(s)? But in that case wouldn't SP release cached elements upon termination of the AppDomain(s)? Not really sure, but what I am sure of is that stopping and disabling the the WRM service allowed inetinfo to completely close when the service was restarted by iisreset. And voilà! My changes!

Incidentally I am running Virtual Server 2005 which is monitored by the "Virtual Machine Manager Agent" service. This service depends on the WRM service. So disabling the WRM service will disable your VMMA service. You might want to let your network admin know you need this disabled or it might reappear.

Friday, June 22, 2007 10:22:26 PM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Creative Commons License