Digital Colony!

Log Events in Classic ASP to File System

Here is some code I created many years ago to log events on a web site using Classic ASP. All the code was placed into an include file called logEvent.asp.
logFile = "C:\SomePath"

Sub LogEvent (pageName, user, message)    
    Const ForWriting   = 2
    Const ForAppending = 8
    
    Set fs = CreateObject("Scripting.FileSystemObject")
    If fs.FileExists(logFile) Then
        Set logFile = fs.OpenTextFile(logFile, ForAppending)            
    Else
        Set logFile = fs.CreateTextFile(logFile, True)
    End If    
    
    logFile.WriteLine(Now() & vbTab & pageName & vbTab & user & vbTab & message)
    logFile.Close
    
    Set logFile = nothing
    Set fs = nothing
End Sub
Add this include line from each page you wish to use the Logging script.
<!--#include virtual="/inc/logEvent.asp" -->
The last step is to actually write to the log file.
Call LogEvent("Home", "Larry King", "Referred from Google")    

Labels: ,

 

Classic ASP Search Engine Friendly Redirects

A while back I posted how to code a search engine friendly redirect in ASP.NET. Recently I've had to go old school and preform this task in Classic ASP. Here is the VBScript version of a search engine friendly redirect.
<% 
   Response.Status="301 Moved Permanently" 
   Response.AddHeader "Location","http://example.com/newpage/"
   Response.End
%>

Labels: , ,

 

ASP Photo Gallery v3

The original ASP Photo Gallery v3 code was written in 2002.

ASP Photo Gallery v3 added thumbnails to ASP Photo Gallery v2. I've removed the v3 code and article from this web site. It was a complete hack that required Classic ASP components that didn't create quality thumbnails. It was not built to handle the much larger image sizes cameras produce today. Therefore I believe it is rarely practical to store and manipulate images on a shared web host, especially using Classic ASP image components. Use a dedicated company like SmugMug or Flickr and then access their APIs to build custom galleries. For a simple gallery using the SmugMug RSS feed, read my tutorial Displaying a SmugMug Gallery with ASP.NET

Labels: ,

 

Building a Database Driven Table in Classic ASP Part 2

The article was written in 2001.

In Part 1, we split the rendering of database-driven HTML tables into 3 parts: Establishing a Database Connection, Getting a RecordSet, and Drawing the TABLE. In this part, we will clean up the column headers, add sorting, and paging.

Renaming Columns

Sometimes you'll want to have the table columns named differently than the columns in the database. Fixing this is as a simple as modifying the SQL query. By aliasing the fields in the query, we can rename the columns.
' original query that renders ugly column headers 
sSQL = "SELECT E.fname, E.lname, D.name FROM Employee E, Department D WHERE E.deptId = D.deptId " 

' aliased query that generates nice column headers 
sSQL = "SELECT E.fname AS FirstName, E.lname AS LastName, D.name AS Department " &_ 
"FROM Employee E, Department D WHERE E.deptId = D.deptId "

Sorting

We can add generic sorting functionality to our table with just a few lines of reusable code. The first version of the sort doesn't require javascript and will work on every browser.
<% 
sSQL = "SELECT CompanyName, ContactName, ContactTitle, City FROM Customers " 
' the sort sequence will be passed in from the querystring 
sort = Request.QueryString("s") 
If len(sort) > 0 AND IsNumeric(sort) Then 
  sSQL = sSQL & " ORDER BY " & s 
End If
Set rs = Server.CreateObject("ADODB.Recordset") 
Set rs = ac.Execute(sSQL) 
%>
'  Update the column code in the drawTable subroutine 
Response.Write "<tr>" 
intColumn = 1 
strPath = Request.ServerVariables("PATH_INFO") 
For Each field In rs.Fields    
   Response.Write "<th><a href=""" & strPath & "?s=" & intColumn & """>" & field.name & "</a></th>"    
   intColumn = intColumn + 1 
Next 
Response.Write "</tr>" & chr(10)

Paging

With large recordsets, we may only want to return a limited number of records. By limiting the number of records returned, the page renders faster. For a detailed tutorial on ADO paging read Recordset Paging with ADO 2.0 by Michael Qualls. We want to add generic code that will work with any query. Inside the connect.asp file there will be a new subroutine called drawPaging.

Files

View the updated source of table.asp.txt and connect.asp.txt.

Last Words

In Part 1 and her in Part 2, we created some generic code libraries to connect to a database, return a recordset, and then draw an HTML table that supports sorting and paging. This code will allow us to quickly generate reports in ASP.

Labels: , , ,

 

ASP Photo Gallery v2

This article was written in 2001. It has a good example of how Classic ASP can use an XML file as a data source. However, I wouldn't use it today to create a photo gallery. There are much better options out there. With that said, this code is no longer supported.

In the article Creating an ASP Photo Gallery, I showed you how to create a very basic photo gallery using just a few lines of ASP code. It was quick and dirty, however, it had limitations.

The first limitation was that all the photos had to be the same size. You could remove the width and height tags from the image, but that is clumsy coding and browsers always render non-sized images funky. The second problem was that we had to rename the images to a number and then maintain a sequence. If you have 5 images that isn't a problem, if you have 100 it becomes tedious. And the last thing missing was the ability to quickly add titles, descriptions, and alt tags for each image.

Choosing a Weapon

OK, we need to store and maintain data about the picture. We have 3 options. Option 1 - We build data arrays on the actual ASP page and populate it there. This is not a clean design. Ideally we want to keep the data and code separated in order to support code reuse. Option 2 - Create a database. Building a database in Access or MySQL is easy enough, but it's overkill for a single photo gallery. Do we really want to setup connection strings and deal with versioning and licensing issues for such a small dataset? No. Option 3 - XML. With XML we can keep the data apart from the code and we don't need a database.

The XML Image File

The XML image file will capture a sequence number, the file name, image width, image height, short caption, long description, and an image ALT tag.
<?xml version="1.0"?> 
<images> 
  <image num="1" file="me.jpg" width="400" height="302" short="Me" long="Me at  Graceland." alt="photo"/> 
</images>

What Can We Automate?

I don't know about you, but I'm not going to fire up an editor and hack out the XML image file. The code should generate most of the body of the file with the exception of the short, long, and alt descriptions. We can use the FileSystemObject to detect images. From this point, we can use the Microsoft.XMLDOM object to build the XML for the photo gallery. Once that is created, wouldn't it be nice to go through the gallery and assign those values in a form? As for the image width and height, we can use client-side javscript to calculate those values the first time that the gallery is viewed.

Security

Since we don't want other users modifying our image captions, we will assign a password to gallery. The password is passed in the querystring under the parameter k and will open the Admin FORM. Example: gallery.asp?k=password&pic=1. This will also give you the power to make text changes from any browser.

Other Admin Features

Besides saving image attributes, the Admin form will allow you the ability to remove an image. The image is removed from the XML file, not the server. And should you upload additional images or accidently remove an image you need, there is a Detect Images buton which will append an image file found in the image folder that doesn't appear on the XML image file.

Setting Up The Photo Gallery Page

My first goal was to make this code as easy as possible for a non-techie to use. The best way to pull that off is to hide all the gritty file detection and XML manipulation in a separate file. All coding logic is kept in an include file called galleryCode.asp. The 2nd file is the ASP file used to house the image gallery. At the top of this file we need to define the following:

key - This is password you'll use to access the gallery as an administrator. galleryURL - The URL to the photo gallery from the root (the virtual path). galleryASP - The name of the ASP page of the actual photo gallery. galleryImageURL - The URL (virtual path) to the photo gallery images. xmlName - The name of the XML file. xmlPath - This is the full file path of the XML file you will be using. This is the only tricky line. Some web hosts don't give read/write permission on directories accessible via the web server. This variable allows you to define a path to a folder that will permit writing and modifying files. Without such a folder this code won't work, so ask your web host for a read/write folder should this code fail.
<% 
' EXAMPLE PHOTO GALLERY SETUP 
key = "secret" 
' page password to access ADMIN 
galleryURL = "/pix/springbreak/" 
galleryASP = "default.asp" 
'this page 
galleryImageURL = "/pix/springbreak/images/" 
xmlName = "springbreak.xml" 
xmlPath = "c:\readwritefolder\" 
%>

Building the Photo Gallery

In addition to your choice of colors and layout, your photo gallery will make simple calls to functions in the galleryCode.asp file. These include:

backLink, nextLink - This will draw the HREF link to the previous and next image. The user can use a text or image link after the call. Be sure to close out the link with a </a>.

thisShort, thisLong - thisShort returns the assigned short title and thisLong returns the long description.

drawImage(border) - Make a call to this subroutine where you want the image placed. The parameter is the pixel width of the border.

drawSequence(perLine) - This returns a sequential list of all the images linked to a number. This allows the user to bypass using the Next and Previous navigation and jump directly to a particular image. The perLine parameter restricts the number of links per line.

drawAdmin - This call is where you want the Admin form to be placed on the page.

Overview

1 - Define filePaths and password on the top few lines of the gallery page.

2 - Upload gallery ASP page and gallerycode.asp file.

3 - Upload images to server. The directory should be unique to that photo gallery.

4 - View the page.

5 - If this the first time the page has been viewed, the images XML file will be created.

6 - Place the password in the querystring and then a FORM will display for each image.

7 - Update the title, description, and alt tag. Go the the next image; repeat this sequence until you've finished. NOTE: Verbiage is optional. However, you'll still want to go through each image so the image height and width are saved to the XML file.

Download

Download Photos2.zip (includes gallery.asp and galleryCode.asp) if you wish to run the ASP Photo Gallery.

Labels: , , ,

 

Creating an ASP Photo Gallery

This article was written in 2001. Although I wouldn't code a photo gallery this way today, it does demonstrate how it can be done easily in a Classic ASP.

I've never cared for traditional photo albums. They take up too much space, they reside in one location (unless copies are made), and after a few years go by they get buried in the attic. Digital photo galleries are far superior. Load up some pictures on some remote server, email out the URL, and presto you've got a photo gallery that people can access anytime they want. And no trips up to the attic.

Having a digital camera makes acquiring the photos easy, but constructing a new photo gallery every time you "shoot a roll of film" can be tedious and time consuming. What we need is a template that we can quickly upload the images and have the gallery operational in less than 30 minutes.

Prep the Pictures

Every digital camera is slightly different, but most generate an image that is too large and byte heavy for web viewing. So the first step is to reduce the size of the image and file. There are many tools that will do this. I use PhotoShop or ThumbNailer to create my photo galleries. If you've walked through the above example photo galleries you've probably noticed that all the images are the same size. This is by design. Having the images the same size allows the image to appear in the exact same spot on the screen as we traverse from image to image. It's also going to make our code easier. ThumbNailer can resize an entire folder on images with a single click.

Using your selcted graphic program, reduce the size of the images to the size you would like to use for the gallery. My 2 galleries use the dimensions of 400 width and 303 height. Reduce the size uniformily so the image doesn't appear stretched or squished. This is something the graphic program should be able to assist you with.

Name and Upload

This gallery is going to use numbers to keep track of the position of the gallery, so all images should be numerically named. Starting with "1", name the images: 1.jpg, 2.jpg, 3.jpg, etc. Make sure there are no gaps in the numbering sequence. For each gallery I build, I create a folder and then a sub-folder for the images. Once the folders are ready, FTP the files up.

Let's Code

Adding color, fonts, and logo graphics is something you can do on your own later. Let's get the gallery working first. This code can be dropped into the BODY of the .ASP file. The gallery definitions are at the top.

<% 
' define the number of pictures in your gallery, this will be the highest image number ex: 18.jpg 
numPix = 18 
imageWidth = 400 
imageHeight = 303 
' use the querystring to request the image 
pic = Request.QueryString("pic") 
' if this is the first page, default to picture 1 
If len(pic) = 0 Then pic = 1 
' build BACK functionality, I'm using an image called left.gif. 
If CINT(pic) = 1 Then     
   Response.Write "<a href=""default.asp?pic=" & numPix & """>" 
Else     
   prevPic = pic - 1     
   Response.Write "<a href=""default.asp?pic=" & prevPic & """>" 
End If 

Response.Write "<img src=""images/left.gif"" width=""40"" hspace=""10"" height=""30"" border=""0"" alt=""Go back""></a>"  
' draw selected image 
Response.Write "<img src=""images/" & pic & ".jpg"" width=""" & imageWidth & """ height=""" & imageHeight & """ vspace=""10"" alt=""photo"">" 
' build FORWARD functionality 
If CINT(pic) = numPix Then     
   Response.Write "<a href=""default.asp?pic=1"">" 
Else     
   nextPic = pic + 1     
   Response.Write "<a href=""default.asp?pic=" & nextPic & """>" 
End If 
Response.Write "<img src=""images/right.gif"" width=""40"" height=""30"" hspace=""10"" border=""0"" alt=""Next Photo""></a>" 
Response.Write "<br/>" 
' draw image number list below photo. This allows the user to jump around the gallery. 
For j= 1 to numPix    
 If j = CINT(pic) Then       
   Response.Write j & "  "    
 Else       
   Response.Write "<a href=""default.asp?pic=" & j & """>" & j & "</a> "    
 End If 
Next 
%>

Last Words

This is the down-and-dirty gallery that will work for most situations. In a future article, I'm going to build upon this gallery to add more features. In the meantime, go take some pictures.

Labels: , ,

 

Using ASPHttp To Scrape Data in Classic ASP

The article was written in 2001.

I recently discovered a cool ASP component from Server Objects called AspHTTP. This component allows an ASP page the ability to GET documents via the HTTP protocol. It also can POST data to a remote web page. Why would you need this for an ASP page? The ability to parse data off a web page and place it in your own format is one need. The AspHttp component lets you to pull the remote web page into your code as a string. From there you can use extensive string parsing to extract the data. Copyright Issues

Copyrights are outside the scope of this article. Just be aware that snagging someone else's data may make their legal departments unhappy. If you choose to use someone else's data, getting their permission may be a wise decision. Use this tool for good, not for evil.

Sample Code

<%
Set HttpObj = Server.CreateObject("AspHTTP.Conn") 
HTTPObj.Url = ' enter some URL here 
' fetch the HTML page into the strResult variable 
strResult = HTTPObj.GetURL 
' check for component error 
If Len(HTTPObj.Error) Then 
   Response.Write "ERROR: " & HTTPObj.Error 
Else ' did we retrieve a document? 
  intLength = Len(strResult) 
  If intLength > 0 Then 
    ' proceed with scrape 
  End If 
End If 
%>

Last Words

Data scraping can be an inexact science. If the web site changes their markup, you can find yourself recoding your scrape. Look for an API first before proceeding with a data scrape.

Labels: , ,

 

Building a Database Driven Table in Classic ASP Part 1

This article was written in 2001. The original title was Table Evolution 1 - The Basics

One of the most common tasks an ASP developer will do is populating an HTML table with data. In the years I've been doing ASP coding, I've created hundreds of tables. This article is the first part of a series demonstrating the evolution of moving data from the database to the screen. Before we can streamline the process, let's first understand the basics.

Three Steps

I've isolated three distinct steps to creating database-driven HTML tables.

1. Connecting to the database.

2. Getting a recordset.

3. Drawing the TABLE.

By separating each task, we will be able to reuse code and reduce the number of lines in our application. In the long run this will make your code easier to maintain.

Establishing a Database Connection

There should be one and only one place in your code where you establish a connection to a database. This means placing your connection code in an include file. Rewriting connection code on each ASP page that performs a database connection will come back to haunt you when the server name changes or your boss moves you from SQL Server to Oracle. Write this code once and if it needs updated, it can be done quickly without compromising the application.

Each database has a slightly different connection string. For this example, I'll be using a connection string to SQL Server. For more information on writing ADO connection strings read the article What's in an ADO Connection String? by John Peterson.
<% 
on error resume next 
' Create ADO Connection Object 
Set ac = Server.CreateObject("ADODB.Connection") 
' Build connection string and then Open connection
strSQL7 = "driver={sql server};server=mySQLServer;database=Northwind;uid=mas;pwd=secret" 
ac.Open strSQL7 
' Detect if there was an error connection to the database 
If err.number <> 0 Then 
  Response.Write "There was an error connecting to the database: " 
  Response.Write Err.number & " - " & Err.Description Response.End 
End If 
%>

Getting a RecordSet

There are several ways to get a recordset. You could use ADO to open up a table and then retrieve each field. You could even use a custom component to return a recordset. For this example we are going to use straight SQL.
<% 
sSQL = "SELECT CompanyName, ContactName, ContactTitle, City FROM Customers " 
Set rs = Server.CreateObject("ADODB.Recordset") 
Set rs = ac.Execute(sSQL) 
%> 

Drawing the TABLE

The Recordset holds all the information we need to draw the HTML table. In order to support code reuse, we will create a subroutine to draw the table in an include file.
Sub drawTable(rs, border, cellspacing, cellpadding, width, align) 
If NOT rs.EOF Then 
    Response.Write "<table width=""" & width & """ align=""" & align & """ border=""" & border & """ cellspacing=""" & cellspacing & """ cellpadding=""" & cellpadding & """>" & chr(10)
    Response.Write "<tr>" 
    For Each field In rs.Fields 
        Response.Write "<th>" & field.name & "</th>" 
    Next 
    Response.Write "</tr>" & chr(10) 
    '=== the altRow will allow us to set a different background color for alternate rows 
    altRow = FALSE 
    While NOT rs.EOF 
        If altRow = TRUE Then 
            Response.Write "<tr class=""altRow"">" 
        Else 
            Response.Write "<tr>" 
        End If 
        
        For each field in rs.Fields 
            thisValue = field.value 
            If len(thisValue) = 0 Then thisValue = " "         
            Response.Write "<td>" & thisValue & "</td>" 
        Next Response.Write "</tr>" & chr(10) 
        altRow = NOT altRow 
        rs.MoveNext 
    Wend 
    Response.Write "</table>" 
Else 
    Response.Write "<p>The query returned no data.</p>" 
End If 
End Sub

Last Words

The connection and drawTable code should be placed inside a single include file. Once that is done we can generate database-drive TABLES with a mere 6 lines of ASP code. I also recommend setting up a CSS file to customize the look of the TABLE. This code covers the basics. The Connection and Recordset have more methods and properties, but for this example we don't need them. In Part 2, we will improve upon the table with better column titles, paging and sorting.

Labels: , , ,

 

File Uploading with ASP Components

This article was written in 2001.

Every now and then there comes a time when a web application requires more from a user. It's not enough that the application receives data in the form of user text boxes and drop-down selections. Sometimes, we need the user to upload a file onto the server. But can we trust the user to setup an FTP program, connect to our server, and place the right file into the right folder without touching anything else? Of course not. What we need to a chimp-simple way to allow users to upload a specific type of file into a specific folder.

Requirements

For this scenario I have 3 requirements:

1 - Restrict the user to uploading to a specified directory on my server.

2 - Restrict file size to prevent huge files from filling up my disk space.

3 - Restrict file types. In this example I only will want image files. Any other file type must be rejected.

Using an ASP Component to Handle File Uploads

There are many file upload components that one can purchase or, if you are so inclined, you can roll your own. The 2 most popular ASP file upload components are Software Artisans FileUp and Persists AspUpload. Both handle uploads slightly different. Let's code each of these components to handle the above requirements.

The FORM

The FORM, where the client uploads a file, is identical.
<form name="form" action="upload.asp" enctype="MULTIPART/FORM-DATA" method="POST">
 <input type="file" name="file1"> <input type="submit" value="Upload Files"> 
</form>

Shared Code

Before you pick which component you will be using, here is some common code that both can use. isFileSizeOK handles the file size restrictions. isValidFile is where you'll define what file extensions the client can upload.

intMaxFileSize = 8000 
strUploadFolder = "c:\uploadFolder" 

Function isFileSizeOK(bytes)    
 ' restrict file byte size    
 byteMAX = intMaxFileSize    
 If bytes > byteMAX Then        
   isFileSizeOK = FALSE    
 Else        
   isFileSizeOK = TRUE    
 End If 
End Function 

Function isValidFile(filename)    
 ' define what file types you will permit to upload    
 fileExtension = lcase(right(filename,4))    
 select case fileExtension        
   case ".gif",".jpg",".png","jpeg"            
     isValidFile = TRUE        
   case else            
     isValidFile = FALSE    
 end select 
End Function

Software Artisans FileUp

Sub uploadSA    
 Set up = Server.CreateObject("SoftArtisans.FileUp")    
 up.Path = uploadFolder    
 If NOT up.IsEmpty Then       
   filename = Mid(up.UserFilename, InstrRev(up.UserFilename, "\") + 1)       
   ' restrict file types to upload       
   If isValidFile(filename) Then          
   ' restrict file by size          
       If isFileSizeOK(up.TotalBytes) Then             
         up.Save             
         strUploadStatus1 = "File [" & filename & "] Uploaded Successfully! " & up.TotalBytes            
       Else             
         strUploadStatus1 = "ERROR: File Too Large: " & filename & " (" & up.TotalBytes & " bytes)"          
       End If       
   Else          
      strUploadStatus1 = "ERROR: This File Type is restricted from uploading: " & filename       
   End If    
 End If    
 Set up = Nothing 
End Sub

Persists AspUpload

With AspUpload, you save the file first, then perform checks. If the file fails the checks, then the code deletes it from the server. This component also has a "SaveToMemory" option, which bypasses the write to the disk until instructed. ASPUpload has built-in image size handling and can detect if a file is an image with the .ImageType property, but for this example we'll use the isValidFile function.
Sub uploadPersists    
  Set up = Server.CreateObject("Persits.Upload.1")    
  up.OverwriteFiles = TRUE    
  up.SetMaxSize intMaxFileSize    
  up.Save uploadFolder    
  For Each File in up.Files       
     fileName = File.ExtractFileName       
     If isValidFile(fileName) Then          
        If isFileSizeOK(File.OriginalSize) Then             
           strUploadStatus2 = "File [" & filename & "] Uploaded Successfully! "          
        Else             
           strUploadStatus2 = "ERROR: File Too Large: " & fileName & " (" & File.OriginalSize & " bytes)"             
           File.Delete          
        End If       
     Else          
        File.Delete          
        strUploadStatus2 = "ERROR: This File Type is restricted from uploading: " & fileName       
     End If    
  Next 
End Sub

Last Words

Both components can do a lot more than what I've demonstrated above. For a more complete list of features, check out the online manuals for Persists AspUpload and Software Artisans FileUp.

This tutorial was expanded for DevGuru.com as A Simple ASP File Upload Application

Labels: , , ,

 

COM Informant for Classic ASP

I currently do business with 4 different web hosts. Each of them has a different subset of 3rd-party ASP components installed on their server. Sometimes they are open with which components are installed, sometimes they aren't. Whenever I wanted to test to see if a particular COM object was available, I'd write a quick script. The script would try to create the object via the Server.CreateObject method and then I'd go to the page to see if it returned an error code. No error code meant it was installed and I could start coding my application around that knowledge.

Tedious and Repetitive

After about the 10th script I wrote, it hit me that there probably is a better way to do this. What was needed was a script that tested the most common ASP components and allowed the user to quickly add new ones to the list. Having a bunch of test scripts lying on the file server wasn't optimal. And the last thing you would want is to hard-code all the test cases inside your ASP code. My solution was to have a single page handle the creation, modification, and display of component test cases. The data source would be a single XML file.

Sample comlist.xml file

<?xml version="1.0"?> 
<comlist> 
<com company="Microsoft" name="CDONTS" id="CDONTS.NewMail"/> 
<com company="Persit Software Inc." name="ASPUpload" id="Persits.Upload"/> 
</comlist>

Developers Tool

COM Informant is handy tool to have if your development team creates custom components and then deploys them across multiple servers. What better way to test if a component is installed than viewing a single web page. The top portion of the tool allows the user to add any component name to test list.

screen shot

The Download

COM Informant For Classic ASP consists of sniff.asp, comlist.xml, com.css, and blue.gif. This tool will be ready to run after download. You need to be running IIS in order to download and run COM Informant. Also, if you want to add or delete components from the list, then the comlist.xml file must reside in a directory that has read/write permission. You can modify the location of that file within the first 3 lines of code. The code performs some XML file manipulation, which is outside the scope of this article.

This article was first written in 2001

Labels: , ,

 

Masking Your Email Address

Some of you are probably aware of spiders. They are these little programs that surf the internet looking for data. Some spiders assist search engines in helping you find the web page you are looking for. Those are the good spiders. There also exists evil spiders. They jump from web page to web page looking for email addresses. Once they find one, they send it to a database so someone can send you junk email. Not cool.

Hiding In Plain Sight

What we need is a way to display an email address so the reader of a web page can communicate with the web site, yet we also need to hide the address from the spider. The reader and the spider are looking at the same web page but at differently levels. The reader is looking at the browser's rendering of HTML. The spider is looking at raw HTML. Three ideas come to mind: ASCII codes, server-side mail forms and images. ASCII codes and images will look like email addresses on the screen, but nothing like an email address in the source code of the HTML document.

Method 1: ASCII

In HTML when you place "&#" in front of the ASCII code of a character the browser will write the character not the ASCII code to the screen. And because this article is being viewed by a browser, the code shots are images. The download will have the source in a text format.

The function below accepts an email address as a parameter and returns a masked email address that is made up of ASCII codes. When the browser writes the codes to the screen it will get converted back to text. Although it's possible for a spider to read and convert ASCII codes inside the HTML source, it's probably not that prevalent. The function goes character by character converting the email address. The last step is to merge the masked email address with the HTML mailto: tag. In order to minimize the chances a clever spider might look for the mailto:, this example maskes that word as well.
Function maskEmail(email) 
   For j= 1 to Len(email) 
      maskEmail = maskEmail & "&#" & asc(Mid(email,j,1)) & ";" 
   Next 
   maskEmail = "<a href=""m&#" & asc("a") & ";&#" & asc("i") & ";&#" &_ 
     asc("l") & ";&#" & asc("t") & ";o:" & maskEmail & """>" & maskEmail & "</a>" 
End Function
Then you can call that VBScript function from inside an ASP page.
<p>For more information email me at <%= maskEmail("someEmail@someDomain.net") %>.</p>

Method 2: Server-side Mail Forms

These are the contact forms you see everywhere these days. The user fills out a form, clicks submit, and hopes it gets to somebody. This is great for the recipient, because their email address never appears on the site. However, some users don't trust filling out a form and will withhold feedback. Recently I was trying to open a new account with eFax.com. Their order entry page was down so I filled out a form alerting them to the problem. After detailing the problem the form rejected because I didn't already have an account with them. They didn't have a direct email address listed anywhere else, so I became a customer of one of their competitors.

Method 3: Images

I believe the ASCII method will work for a little while. Eventually as that trick becomes more popular, the spiders will get smarter. Who knows maybe the developer that writes the code for those evil little email spiders is reading this article right now. If we created an image with text of our email, the spiders would be truly be defeated. However creating an image for every email address in PhotoShop would be a hassle. And then what happens when we change fonts when the site gets redesigned? Creating the email images by hand isn't an option. We need to automate.

For this version I extended the ImageToText code sample at yyyZ.net.

Method 4: Chopped Javascript

Now that you have an email image or icon, you may want to assist the users so they click on an email address image it behaves as if it were a hypertext link. This means having their email client launched with the TO line filled out for them. In order for this to happen with the email image, we need to hack out a chopped Javascript function. There are many possible ways to write it. In this example I wrote a function that accepts an email address into 3 parts. It then reassembles the email and launches the email client. How you chop up the email address is up to you. Also feel free to change the sequence of the parameters.
<script language="JavaScript" type="text/javascript">  
function postage(one,two,three){ 
   window.location = 'mailto:'+one+two+three;} 
</script> 
<img src="123.jpg" alt="email" width="100" 
    height="40" border="0" 
    onmouseover="this.style.cursor='hand'" 
    onclick="postage('larryking@c','n','n.com');"/> 

Lab Demos

Mask Email Image Generator (Email Obfuscator)

Mask Email ASCII Generator

PHP Version of Masking Email Addresses

Labels: , , , ,

 

Blogger Label List for FTP Accounts (Classic ASP)

For an overview on Blogger Label Lists read Blogger Label List for FTP Accounts (ASP.NET). Below is the code used to create a Label List using Classic ASP with VBScript.

Modify the first line to point to your label folder. Then correct the spelling of bl0gger-labels. It is purposely misspelled as to not throw off the count on this page. Save this code with a .asp file extension and then use a server-side include to place in onto your page template.
'-- add your label directory here
labelDir = Server.MapPath("/myblog/labels/") 
'-- Check for Directory
Set FSO = Server.CreateObject("Scripting.FileSystemObject")
If FSO.FolderExists(labelDir) Then
    Response.Write "<ul>"
    Set labelFolder = FSO.GetFolder(labelDir)
    Set labelBlogs = labelFolder.Files
    For each label in labelBlogs
        Set labelFile = FSO.GetFile(label)
        Set labelStream = labelFile.OpenAsTextStream (1, -2)    
        iLabelCount = 0
        '-- Read the file line by line
        Do While Not labelStream.AtEndOfStream                
            Line = labelStream.readline
            LineCount = Sgn(InStr(Line,"bl0gger-labels"))                
            iLabelCount = iLabelCount + LineCount
        Loop                                
        Response.Write "<li>" + Replace(labelFile.Name,".asp","") & " (" + CSTR(iLabelCount) & ")</li>"
        Set labelStream = nothing
        Set labelFile = nothing
    Next
    Response.Write "</ul>"
Else
    Response.Write "<p>No labels in folder: " & labelDir & "</p>"
End If
Set FSO = nothing    
Below is an example of using a server-side include inside a Classic ASP page.
<div id="divLabelList">
<!--#include virtual="/inc/theme.asp"-->
</div>
Back in the day, Ev would have been proud of me.

Labels: , ,

 

SQL Injection - Case Study

Well I got nailed today. My site INeedCoffee.com which is written using Classic ASP fell victim to a SQL Injection attack. The damage was limited to just one column in a table of nine rows.

Textbook SQL Injection Attack

Almost every example I've seen that explains SQL Injection shows how the WHERE clause is vulnerable to querystring manipulation.
sAuthorID = Request.QueryString("AuthorID")
sSQL = "SELECT firstName, lastName FROM Author WHERE authorID = " & sAuthorID

This query can be nailed with a single quote and an OR clause to dump the entire table to the screen. Adding 1' or '1'='1 to the querystring now yields this SQL.
SELECT firstName, lastName FROM Author WHERE authorID = 1 OR 1=1

Attacked Via a Column Sort

The attack I received came from appending additional SQL to ORDER BY clause. By clicking on the column header on the contributor page a column number is passed to the querystring. This was getting concatenated to a SQL statement.

EXAMPLE: http://ineedcoffee.com/by/michael_allen_smith/?s=1

The server-side ASP code looked like this:
sort = Request.QueryString("s")
If Len(sort) Then
    sSQL = sSQL & " ORDER BY " & sort
Else
    sSQL = sSQL & " ORDER BY C.Created DESC " 
End If

The hacker guessed the name of the table and one of the column names. From there it was an easy to hack together a querystring that looked like this:

http://ineedcoffee.com/by/michael_allen_smith/?s=1;UPDATE Section SET Name='hacked'

A simple semicolon followed by an UPDATE statement. This was just one column on a minor lookup table. I'm grateful that he/she didn't use the DROP TABLE command in the right sequence.

Locking It Down

Although ASP.NET has a nice feature to add parameters to dynamic SQL, Classic ASP is still best protected with stored procedures. Now the sort parameter goes into a stored procedure which can only perform SELECT. Prior to entering the stored procedure I'm now performing tests on length of the querystring (LEN) and determining if is an integer (IsNumeric).

Labels: , ,

 

Sending Email Using CDONTS

ASP developers can use the CDONTS technology, which is built into Microsoft's IIS web server to send server-side email. CDONTS, which is also referred to as CDO, stands for Collaboration Data Objects. Once an SMTP service is setup, CDONTS allows the sending of email from ASP script. Setting up an SMTP service is outside the scope of this article.

Basic Text Email

The simplest email is the plain text message. Below, is sample code for sending a plain text email. The following example will send a message to Huey, carbon-copy Louie, and blind-carbon-copy Dewey.
on error resume next 
Set Mailer = Server.CreateObject("CDONTS.NewMail") 
Mailer.From = "Duck Newsletter<newsletter@duck.com>" 
Mailer.To = "huey@duck.com" 
Mailer.CC = "louie@duck.com" 
Mailer.BCC= "dewey@duck.com" 
Mailer.Subject = "Duck Newsletter" 
mailBody = "-- newsletter text --" 
Mailer.Body = mailBody 
Mailer.Send 
If err.num Then    
    Response.Write "CDONTS Error: " & err.num & " - " & err.description
End If

Wrapping it up with HTML

Sending HTML email is just another variation on text email, since HTML is merely marked up text. Other than the HTML tags, the only difference is specifying a valid DOCTYPE in the first line of the email. There a few valid DOCTYPES to chose from when constructing an HTML email. Web Design Group has put out a good article that describes the differences: Choosing a DOCTYPE. It also advised to include a META tag describing the Content-Type. In this example the Content-Type is Latin1 HTML.
on error resume next 
Set Mailer = Server.CreateObject("CDONTS.NewMail") 
Mailer.From= "Duck Newsletter<newsletter@duck.com>" 
Mailer.To = "huey@duck.com" 
Mailer.Subject = "Duck Newsletter" 
' BodyFormat and MailFormat should be set to 0 for HTML emails. 
Mailer.MailFormat = 0 
Mailer.BodyFormat = 0 
Dim mailBody
' 1-add DOCTYPE to mailBody
' 2-add HTML HEAD TITLE and BODY tags to mailBody
' 3-construct message using valid HTML inside mailBody
' 4-close BODY and HTML tags to mailBody
Mailer.Body = mailBody 
Mailer.Send 
If err.num Then    
     Response.Write "CDONTS Error: " & err.num & " - " & err.description 
End If

More CDONTS Features

You may want to use CDONTS for two other things: sending a file attachment and assigning a message priority. You can also provide an optional 2nd parameter to name the file attachment. Defining priority is done via the Importance property. The three posible values are: High (2), Normal (1), or Low (0). The default is Normal. This example will send a file attachment and mark the message as HIGH priority.

Last Words

CDONTS is a very robust way to send server-side email. I've used it to send 1000 newsletters on 1 ASP page without error. If you plan to use CDONTS to send a large number of newsletters from a single page, be sure to add "Server.ScriptTimeout = 99999999" at the top of the ASP page. This will prevent IIS from timing out the page while the messages are being sent.

This article was originally written in 2001.

Labels: ,

 

Digital Colony Copyright © 1999-2009 XHTML   508
This site uses Blogger, which is not 100% XHTML compliant.
Try...Catch Disclaimer: For brevity many examples do not include error handling. That is your responsibility.