Getting detailed ASP errors under IIS 7.0

Some time ago I was doing maintenance work on some classic ASP pages and couldn’t get detailed error reports sent back to the browser. Using Internet Explorer I disabled “Show friendly HTTP error messages” but still received the generic HTTP 500 type error messages from IIS 7.0 running under Windows Server 2008. The same also occurred under FireFox. The modifications I was making to the ASP pages were fairly limited so I gave up on trying to get it to work and ending up sticking with examining the IIS logs on the server for debugging information.

Today I started work on maintaining some more complex ASP pages developed by someone else under Vista x64 and IIS 7 and encountered the same problem. I also noticed Vista locks the IIS log files while the web server is running which further added to my determination to get ASP errors including line numbers back to the browser. It appears setting “Send Errors To Browser” in “Debugging Properties” for the ASP properties doesn’t work at a virtual directory level, it only works when enabled at the root level for the entire site. Enabling it at the higher level got back the detailed error reports as per the default bahaviour of IIS 6.

ASP site map generator

This is a simple ASP site map generator that I wrote some time ago using VBScript, it’s for a human-readable sitemap not the XML sitemaps used by Google and other search engines. It uses the Scripting.FileSystemObject to scan the same directory level that the ASP is placed at and returns a link to any htm or html pages found along with the last modification time and the title of the page. It was fairly quickly written so the parsing used for the title tag is just to suit the format I was using and it might need some other tweaking to suit your own site.

I’ve just loaded a copy at http://www.satsleuth.com/site_map.asp so you can take a look at the script in action. Because I don’t actively use the script anymore I haven’t updated to XHTML 1.0 Strict so on the W3C Markup Validation Service it will only pass as HTML 4.01 Transitional. Currently presentation elements are embedded in the HTML rather than a CSS style sheet. Here is the main logic for the page:

<table border="1">
<tr><td><b>Page name</b></td><td ALIGN="RIGHT"><b>Modified</b></td><td><b>Title</b></td></tr>
<% 
	Dim objFSO, objFolder, objFiles, objFile, strFile, Title, CurLine, LCaseLine, TitlePos, PageCount, FontColour

	Private Sub GetTitle(FileName)
		Dim fso, file, strPhysicalPath

		Title = ""
		set fso = Server.Createobject("Scripting.FileSystemObject") 
		strPhysicalPath = Server.MapPath(FileName) 
		set file = fso.opentextfile(strPhysicalPath, 1)
		do until file.AtEndOfStream or Len(Title) > 0
			CurLine = file.ReadLine
			LCaseLine = LCase(CurLine)
			TitlePos = InStr(LCaseLine, "<title>")
			if TitlePos then
				Title = Right(CurLine, Len(CurLine) - TitlePos - 6)
				Title = Left(Title, InStr(LCaseLine, "</title>") - 8 )
			end if
		loop
		file.close
	End Sub

	Set objFSO = Server.CreateObject("Scripting.FileSystemObject") 
	Set objFolder = objFSO.GetFolder(Server.MapPath("."))
	Set objFiles = objFolder.Files
	PageCount = 0
	For Each objFile in objFiles 
		strFile = LCase(objFile.Name) 
		If Right(strFile, 4) = ".htm" or Right(strFile, 5) = ".html"  then
			GetTitle(objFile.Name)
			if Len(Title) > 0 then
				PageCount = PageCount + 1
				if objFile.DateLastModified >= Date - 7 then
					FontColour = "<font color=""#FF0000"">"
				else
					FontColour = "<font color=""#000000"">"
				end if
				Response.Write("<tr><td><a href=""" & strFile & """>" & _
					strFile & "</a></td><td align=""RIGHT"">" & _
					FontColour & DateValue(objFile.DateLastModified) & "</font>" & _
					"</td><td>" & Title & "</td></tr>")
			end if
		end if
	next
	Response.Write("<tr><td>&nbsp;</td><td>&nbsp;</td><td><b>Total pages: " & PageCount & "</b></td></tr>")
%>
</table>

Creating compressed ZIP files using VBScript

SQL/Server database backups tend to compress fairly well, in my experience typically down to around 25% of the original file size. However I discovered backup data compression is only a feature of SQL/Server 2008 Enterprise Edition and it’s not a feature that inspired me to spend thousands extra on a license. I’ve attached a VBScript procedure that I wrote to automatically compress my SQL/Server backups but of course it can be used for any application where you’d like to compress the entire contents of a directory automatically.

I can’t take credit for the innovative technique of creating a ZIP header in code to make Windows create a compressed ZIP folder. That was a part of a code snippet I found elsewhere quite some time ago. For some reason the MoveHere method of Shell.NameSpace didn’t seem to remove the source file so when move mode is selected I stuck with deleting the source file afterwards. This version displays ZIP compression progress in a dialog box but you could use “ZipFile.CopyHere InFilename, 4” to disable the progress dialog box.

' VBScript to move or copy all files in a folder to a compressed ZIP file

Option Explicit
Const MoveMode = False
Const BackupDir = "E:\DB Backups"
Const TimeoutMins = 10 ' Timeout for individual file compression operation

Sub MoveToZip(InFilename, OutFilename)
	Dim FSO : Set FSO = CreateObject("Scripting.FileSystemObject")
	Dim Timeout : Timeout = 0
	FSO.CreateTextFile(OutFilename, true).WriteLine "PK" & Chr(5) & Chr(6) & String(18, 0)
	Dim Shell : Set Shell = CreateObject("Shell.Application")
	Dim ZipFile: Set ZipFile = Shell.NameSpace(OutFilename)
	ZipFile.CopyHere InFilename
	Do Until ZipFile.items.Count = 1  or Timeout > TimeoutMins * 600
		Wscript.Sleep 100
		Timeout = Timeout + 1
	Loop
	If MoveMode and ZipFile.items.Count = 1 Then FSO.DeleteFile(InFilename)
	Set Shell = Nothing
	Set FSO = Nothing
	Set ZipFile = Nothing
End Sub

Dim FSO : set FSO = CreateObject("Scripting.FileSystemObject")
Dim Folder : Set Folder = FSO.GetFolder(BackupDir)
Dim Files : Set Files = Folder.Files
Dim File
For Each File In Files
	If InStr(UCase(File.Name), ".ZIP") = 0 Then
		MoveToZip BackupDir & "\" & File.Name, BackupDir & "\" & FSO.GetBaseName(File.Name) & ".zip"
	End If
Next

Posting to a forum programmatically using Delphi

I’m a member of a web site where we run various games in the forums and for several that can be tedious to keep track of manually I’ve written some code in Borland Delphi to automate the task. The forum itself is phpBB and I use the web browser component included with Delphi to edit posts directly in the forum. Being able to post to a website as though a user submitted a form is handy for these sorts to tasks where you don’t have access to the underlying forum database. I’ve posted the more interesting parts of the code that format the request and setup the correct headers:

var
  strData: String;
  PostData, Headers: OleVariant;
...................
strData := 'message=' + HTTPEncode(MessageData) + '&notify=on&mode=editpost&p=' + IntToStr(PostID) + '&post=+Post+';

PostData := VarArrayCreate([0, Length(strData) - 1], varByte);

for i := 1 to Length(strData) do
  PostData[i-1] := Ord(strData[i]);

Headers := 'Content-Type: application/x-www-form-urlencoded' + #10#13;

WebBrowser.Navigate('http://example.com/posting.php?mode=reply&t=' + MainTopicID.Text, EmptyParam, EmptyParam, PostData, Headers);

GPS / Asset tracking AJAX web page

I’ve just completed an AJAX based GPS tracking page for a client based on the OpenLayers Javascript library using the OpenStreetMap map tile server for data, although it can be easily be adapted to other data sources. The following page offers a login to the demonstration system for evaluation:

GPS tracking page

Data is served from an SQL database with a modular set of stored procedures translating data from its raw format allowing integratation with a wide range of data sources and GPS tracking devices. Columns displayed can be customised on a per-user basis and dynamically generated SQL queries allow great flexibility in the data generated. Please contact me if you are interesting in licensing the software for your own site.

MySQL: Access denied for user ‘root’@’localhost’ (using password: YES)

After Googling and reading through all the usual reasons for this error when trying to connect to a MySQL database I was starting to get frustrated because I could connect to MySQL server using several GUI applications so knew my root password was correct. The problem only cropped up when I wanted to launch MySQL from a bash script to execute a stored procedure via a cron job. When I stepped back and took a look it turned out to be a fairly obvious problem, I was trying as per this example (obviously not the real password):

mysql -u root -pABC$123

I normally like to include a few special characters in my passwords and use more than eight characters to negate the possibility of them being cracked using reasonable length rainbow tables. Of course the dollar sign was causing bash environment variable substitution so it was simply a matter of prefixing the dollar sign with a backslash:

mysql -u root -pABC\$123

OSM planet.osm disk requirements

After loading the planet.osm file (planet-090916.osm.bz2) into an Ubuntu 9.04 virtual machine the total disk usage was 133GB after the import. The total dropped to 115GB after doing a VMWare shrink operation however it looks like to account for future near-term expansion of the OSM data I should allow for at least 200GB of storage. I’d originally hoped to store the data on a small but fast SAS 15K RAID array on my server but the current version will only barely fit so I’ll need to consider other alternatives. It looks like costs are dropping fairly fast for some of the larger 15K SAS drives so I might defer the decision for a few months as the system isn’t required for production until the start of the year. Meanwhile I’ll just slip a 1TB SATA drive into the server for testing.

SQL convert integer to hexadecimal string

The following is a small SQL/Server function to convert an integer to a two digit hexadecimal string. I’m using it as part of a GPS mapping application to display the poll type from an Inmarsat D+ terminal.

CREATE FUNCTION ConvertToHex2
(
@pNumber int
)
RETURNS varchar(2)
AS
BEGIN
DECLARE @HexDigits varchar(16)
DECLARE @Digit1 int
DECLARE @Digit2 int
DECLARE @ResultVar varchar(2)

IF @pNumber IS NULL
SET @ResultVar = NULL
ELSE BEGIN
SET @HexDigits = ‘0123456789ABCDEF’
SET @Digit1 = @pNumber / 16 + 1
SET @Digit2 = @pNumber % 16 + 1
SET @ResultVar = SUBSTRING(@HexDigits, @Digit1, 1) + SUBSTRING(@HexDigits, @Digit2, 1)
END
RETURN @ResultVar
END

OSM (OpenStreeMap) planet.osm statistics

I’ve recently started importing the planet.osm file into a postgresql database using the osm2pgsql utility. When I started I couldn’t find any up-to-date statistics on the number of primitives in the database to estimate the time it would take so I’ve posted them here. I’m running on a machine with only 2GB RAM allocated under VMWare and a slow 7200RPM 1TB drive and it looks like the process will take around 5 days to complete. The final stages of creating indexes on the tables seems to be taking as long as the initial import.

Reading in file: /home/mapnik/osm/planet-090916.osm.bz2
Processing: Node(434807k) Way(33142k) Relation(212k)
Node stats: total(434807934), max(497541099)
Way stats: total(33142281), max(40900619)
Relation stats: total(212012), max(253016)

SQL/Server function to format integer IP address

The following is a small SQL/Server function to convert an IP address stored as an integer (actually bigint) into a human-readable string in the format ‘1.2.3.4’. I’m using it as part of a geolocation by IP address project.

CREATE FUNCTION IP2String (@IPAddress bigint)
RETURNS varchar(15)
AS
BEGIN
DECLARE @IP1 bigint
DECLARE @IP2 bigint
DECLARE @IP3 bigint
DECLARE @IP4 bigint
DECLARE @ResultVar varchar(15)

SET @IP1 = @IPAddress / 16777216
SET @IPAddress = @IPAddress – @IP1 * 16777216
SET @IP2 = @IPAddress / 65536
SET @IPAddress = @IPAddress – @IP2 * 65536
SET @IP3 = @IPAddress / 256
SET @IPAddress = @IPAddress – @IP3 * 256
SET @IP4 = @IPAddress

SET @ResultVar = CAST(@IP1 AS varchar(3)) + ‘.’
+ CAST(@IP2 AS varchar(3)) + ‘.’
+ CAST(@IP3 AS varchar(3)) + ‘.’
+ CAST(@IP4 AS varchar(3))
RETURN @ResultVar

END