Creating IIS 6 Directories by C#

I’ve been writing this for a while today and couldn’t figure out why I couldn’t get the application to create within IIS. The virtual directory created without problems, but after that nothing happened. It turns out that the msdn library was incorrect!

Here is a working IIS 6 virtual directory creator. The only paramaters that need to be changed are in the // Set comment.

// IIS 6 Virtual Directory Builder and Configuration
// http://r-dunn.co.uk/ieatpenguin/computing/software/962/
// Please feel free to use and distribute

// Set variables: Site_Name,Path_to_wwwroot,Virtual_Directory_Name,Path_to_virtual_dir
// Change the port number after "ServerBindings" if you wish to use a different port.

using System;
using System.IO;
using System.DirectoryServices;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections;

namespace System_DirectoryServices_DirectoryEntry_ConfigIIS
{
  class Program
  {
    static void Main(string[] args)
    {
        CreateSite("IIS://Localhost/W3SVC", "555", "Site_Name", "Path_to_wwwroot");
        SetSingleProperty("IIS://Localhost/W3SVC/555", "ServerBindings", ":8080:");
        CreateVDir("IIS://Localhost/W3SVC/1/Root", "Virtual_Directory_Name", "Path_to_virtual_dir");
    }

    static void CreateSite(string metabasePath, string siteID, string siteName, string physicalPath)
    {
      //  metabasePath is of the form "IIS://<servername>/<service>"
      //    for example "IIS://localhost/W3SVC" 
      //  siteID is of the form "<number>", for example "555"
      Console.WriteLine("\nCreating site {0}/{1}, mapping the Root application to {2}:", 
          metabasePath, siteID, physicalPath);

      try
      {
        DirectoryEntry service = new DirectoryEntry(metabasePath);
        string className = service.SchemaClassName.ToString();
        if (className.EndsWith("Service"))
        {
          DirectoryEntries sites = service.Children;
          DirectoryEntry newSite = sites.Add(siteID, (className.Replace("Service", "Server")));
          newSite.Properties["ServerComment"][0] = siteName;
          newSite.CommitChanges();

          DirectoryEntry newRoot;
          newRoot = newSite.Children.Add("Root", "IIsWebVirtualDir");
          newRoot.Properties["Path"][0] = physicalPath;
          newRoot.Properties["AccessScript"][0] = true;
          newRoot.CommitChanges();

          Console.WriteLine(" Done. Your site will not start until you set the ServerBindings or SecureBindings property.");
        }
        else
          Console.WriteLine(" Failed. A site can only be created in a service node.");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Failed in CreateSite with the following exception: \n{0}", ex.Message);
      }
    }

    static void SetSingleProperty(string metabasePath, string propertyName, object newValue)
    {
      //  metabasePath is of the form "IIS://<servername>/<path>"
      //    for example "IIS://localhost/W3SVC/1" 
      Console.WriteLine("\nSetting single property at {0}/{1} to {2} ({3}):",
          metabasePath, propertyName, newValue, newValue.GetType().ToString());

      try
      {
          DirectoryEntry path = new DirectoryEntry(metabasePath);
          PropertyValueCollection propValues = path.Properties[propertyName];
          string oldType = propValues.Value.GetType().ToString();
          string newType = newValue.GetType().ToString();
          Console.WriteLine(" Old value of {0} is {1} ({2})", propertyName, propValues.Value, oldType);
          if (newType == oldType)
          {
              path.Properties[propertyName][0] = newValue;
              path.CommitChanges();
              Console.WriteLine("Done");
          }
          else
              Console.WriteLine(" Failed in SetSingleProperty; type of new value does not match property");
      }
      catch (Exception ex)
      {
          if ("HRESULT 0x80005006" == ex.Message)
              Console.WriteLine(" Property {0} does not exist at {1}", propertyName, metabasePath);
          else
              Console.WriteLine("Failed in SetSingleProperty with the following exception: \n{0}", ex.Message);
      }
    }

    static void CreateVDir(string metabasePath, string vDirName, string physicalPath)
    {
      //  metabasePath is of the form "IIS://<servername>/<service>/<siteID>/Root[/<vdir>]"
      //    for example "IIS://localhost/W3SVC/1/Root" 
      Console.WriteLine("\nCreating virtual directory {0}/{1}, mapping the Root application to {2}:",
          metabasePath, vDirName, physicalPath);

      try
      {
        DirectoryEntry site = new DirectoryEntry(metabasePath);
        string className = site.SchemaClassName.ToString();
        if ((className.EndsWith("Server")) || (className.EndsWith("VirtualDir")))
        {
          DirectoryEntries vdirs = site.Children;
          DirectoryEntry newVDir = vdirs.Add(vDirName, (className.Replace("Service", "VirtualDir")));
          newVDir.Properties["Path"][0] = physicalPath;
          newVDir.Properties["AccessScript"][0] = true;

          // Create Application, set appropriate documents and permissions
          newVDir.Invoke("AppCreate", new object[1] { 1 });
          newVDir.Properties["AppFriendlyName"].Value = vDirName;
          newVDir.Properties["EnableDirBrowsing"][0] = false;
          newVDir.Properties["AccessRead"][0] = true;
          newVDir.Properties["AccessExecute"][0] = true;
          newVDir.Properties["AccessWrite"][0] = false;
          newVDir.Properties["EnableDefaultDoc"][0] = true;
          newVDir.Properties["DefaultDoc"][0] = "ReportsIsapi.dll?Login,ReportsIsapi.dll";
          newVDir.Properties["AspEnableParentPaths"][0] = true;
          newVDir.CommitChanges();

          Console.WriteLine(" Done.");
        }
        else
          Console.WriteLine(" Failed. A virtual directory can only be created in a site or virtual directory node.");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Failed in CreateVDir with the following exception: \n{0}", ex.Message);
      }
    }
  }
}

Don’t forget to use a double ‘\\’ in your pathnames to avoid the compiler interpreting them as an escape sequence. I certainly did.

Backing up The Latest File – Name Unknown

It’s a common enough issue for me that I want to be able to backup the latest backup of a database, and using variables to set date and time I never quite know what the filename of the backup will be.

::copy latest file to network server
set LF=
for /F %%i in ('dir /Od /b *.sql') DO set LF=%%i
:
echo%TIME% on %DATE%: Latest Backup is %LF% >> Backup.file
:
xcopy /Y %LF% \\networkserver\bugzilla

My solution is to sort the backup directory by date, and create a variable based on the filename of the most recent file.

I then echo the time and date to a file (Backup.file) so that I have a log of the backup process that I can check should I need to in the future.

Finally, the script copies the latest backup to a network location.

This was used in conjunction with my bugzilla backup script, hence the directory names, but it can of course be used for anything.

Automated Backup of Bugzilla Database on Windows

Bugzilla isn’t really designed to be used on Windows, but once set up its fairly easy to administer.

One obviously important thing is backing up the database. Using the niftily built in mysqldump I used the following solution. Note this assumes default settings were used in the installation of Bugzilla.

First, add mysql to the windows path if it hasn’t been done so already, by running up a command prompt:

PATH=%PATH%;C:\Program Files\Bugzilla\mysql\bin\

I then use a command file to create the backup dump, and then append the date and time to the file name. Note I have also created a user (backup) on the bugzilla database with a limited set of privileges (Select, Lock Table, Show Databases, Event).

rem commmand to dump the database to file
mysqldump -ubackup -pbackup Bugs > c:\bugsbackup\bz.sql

rem set date and time into useable file format
set _my_datetime=%date%_%time%
set _my_datetime=%_my_datetime: =_%
set _my_datetime=%_my_datetime::=%
set _my_datetime=%_my_datetime:/=_%
set _my_datetime=%_my_datetime:.=_%

rem rename file
ren "c:\bugsbackup\bz.sql" bz_%_my_datetime%.sql

This command file can of course be added to scheduled tasks and run as frequently as you want.

SecurityTools

This is a nasty little program that masquerades as a security program that has found lots of viruses on your PC, and will clean them if you unlock it – eg if you hand over your credit card details.

The easiest fix I’ve found is using ComboFix, and then deleting the [random letter/numbers] folders found under ‘\Documents And Settings\All Users\’.

Windows Home Server v2 (Vail) is dead in the water

I’m a big fan of windows home server. They failed to deliver on some things, such as Media Centre integration, but that doesn’t really bother me. It’s a decent piece of server kit with a nice UI built over the top of Server 2008.

The best feature of it has to be Drive Extender, a disk management tool that allows you to pool your drives, of any size, and provides one click duplication, making installing new disks and taking backups a breeze for the home user. The clue is in the title: Windows Home Server.

Vail has been in the works for a while, and a recent post on the WHS blog has announced that the number one feature has been stripped. I’m fortunate enough, I guess, in that I understand how RAIDs work, and know how to set them up. However, I use WHS because I don’t have too. I spend enough time at work with computers that I don’t want to at home.

Absolutely unbelievable. Microsoft manage to alienate their core customer base (of this product) in one fell swoop.

Still, at least it saves me the cost of the upgrade.

Userenv Error 1500/1505/1508 – Profile Unable to Login

Windows cannot log you on because your profile cannot be loaded. Check that you are connected to the network, or that your network is functioning correctly. If this problem persists, contact your network administrator.

This annoying error message plagued me for quite a long time until I eventually found the solution. The problem appears to be with the pagefile not being able to perform user profile allocation properly.

After a fair bit of searching and tinkering, I eventually hit upon the following solution:

  • First set the pagefile to by managed by the system.
  • Load regedit, and find: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management
  • Create a new DWORD called PoolUsageMaximum. Set this to 60 (Decimal).
  • Create/Modify PagedPoolSize (DWORD) to ffffffff (Hex).
  • Reboot.

What this does is simply force the Memory Manager in windows to attempt to trim the pagefile when it is 60% full, rather than its default 80%. This therefore starts the pagefile trim earlier, hopefully enabling the computer to cope with surges in memory demand more efficiently.

MSSQL – How To Output To A File

There is a very, very simple one liner within MySQL to dump the results of a query into a text file. Nothing so simple exists within MSSQL, but there are fairly easy workarounds. Using BCP, one can use the xp_cmdshell to pass the results into the desired file.

use <em>DatabaseName</em>
go

declare @FileName varchar(50)
declare @bcpCommand varchar(2000)

set @FileName = REPLACE('c:\temp\bcp\postcodes_'+CONVERT(char(8),GETDATE(),1)+'.csv','/','-')

set @bcpCommand = 'bcp "select left(postcode,4), count(*) from DatabaseName..TableName where customersequence = 0 group by left(postcode,4) order by count(*) desc" queryout "'
set @bcpCommand = @bcpCommand + @FileName + '" -U username -P password -c'

EXEC master..xp_cmdshell @bcpCommand

This dumps a load of postcode information, based on usage, into a csv file in c:\temp\bcp. What the query is doesn’t really matter, just the bcp commands to provide us with the results in a file of our choosing (.csv in this case).

The @FileName command simply appends the current date to the file in order to keep track of queries.

Mounting a network drive in Linux (Ubuntu)

This is a very simple thing to do, it isn’t however as simple as one might think if you are coming from a Windows background, and are used to mapping a network drive from the Tools menu. As a note this is a guide for Ubuntu, although I have it working fine on both Ubuntu and Fedora, use the appropriate package manager/command line for Fed and the rest is the same.

First, we need to make sure that samba is installed:

sudo apt-get install smbfs

Next, we need to make a directory to mount the drive too. As an example, I’ve just reinstalled my Ubuntu (and Fedora) distribution, and so want to map the music drive on my server. I chose /media/ as the logical place to stick my network drives:

sudo mkdir /media/music

Next we need to tell the file system table where the drives are, and where to mount them. We also need to include our login credentials (will cover this later).

gksudo gedit /etc/fstab

Scroll to the bottom of the file and add the following:

#Mounting Network Drives
//SERVER/SHARE-NAME /MOUNT-POINT smbfs credentials=/credentials-file-location

To make the above make a bit more sense, here is my configuration:

//192.168.1.50/Music /media/music smbfs credentials=/home/russell/credentials.smbcredentials
//192.168.1.50/Videos /media/videos smbfs credentials=/home/russell/credentials.smbcredentials
//192.168.1.50/Software /media/software smbfs credentials=/home/russell/credentials.smbcredentials

What this will do is to check within the credentials file (more on this at the bottom) your username and password for your server (I am running a Windows Home Server as an example).

Next, we need to make the filesystem mount the drive, which we do simply with:

sudo mount -a

Finally, we need to make that credentials file. Simply navigate to your chosen directory (I stuck it in my /home/russell directory for ease), create a new file with the following information:

username=username
password=password

And save it with the same filename you gave the /fstab/. Thats it.

EU Probes Google Antitrust Case

This is long, long, LONG overdue.

I was going to write a few things, but this comment on the Times Website succinctly summed it up, so thanks to “I.M. Jolly”.

The difference between “do no Evil..” Google and Microsoft is, MS are a damn sight less hypocritical about their monopoly postion. And abuse thereof.

If the simple fact that “Google is rapidly increasing how much it spends on lobbying in the United States..” doesnt tell you anything, I dont know what else will – ok Google fanboys, you can now go back to your Google searches for everything, Your GMail with targeted ads based on the content of your mails and put your head back under the pillow. For the rest of us, I am sure we can agree that some scrutiny of a company which has basically won control of the internet, while gaining a very, very large amount of personal users data and their surfing habits, is entirely overdue.

Or, maybe, as the Google CEO (Eric Schmidt) himself says, “”If you have something that you don’t want anyone to know, maybe you shouldn’t be doing it in the first place..” – unless of course, that knowledge is personal details of his, obtained by CNET Journalists only via Google searches. Google blacklisted CNET for about a year over this, then again, they really do have to the power to be both evil and hypocritical, whenever it suits. The public have somewhat less choice in blacklisting which Information Google collects from them.

However, Google only use the info they have collected on YOU to sell ads, dont they? No problem there, then.