Sunday, December 03, 2006

Free Online Keyword Research Tools

I decide to post this list since I can't find a single list that contains all the tools I use (which isn't many):

Google AdWords Keyword Tool, Keyword Sandbox Tool Good tool but it's Google after all, so keep in mind that you and everyone else on the planet is using it.

MIVA Keyword Generator Note this is from the UK, so spellings might be non-USA

Overture Keywords Suggestion Tool Same problem here as with Google - any keywords you generate here will probably not be unique. Also somewhat of a pain, because it it alphabetizes the keyword varients it returns. So "cheap tennis racket" varients ceom out as "cheap racket tennis", "american express credit card" varients come out as "american card credit express" etc. Annoying

Search Engine Keyword Tracker & Keyword Ranking Tool This on is hand because it generates some Wordtracker words as well as the Overture ones. That's what I use it for. For some reason, the Overture lookup here sometimes seems to be faster than the actual Overture site.

7Search Keyword Suggestion Tool. Brings back 100 at a time. Good varients here.

The Miva and 7Search tools are pretty good, and probably not as widely used as the Google and Overture tools. I tend to use them for my main research and then augment with Google and Overture.

I'm pretty sure Microsoft has one too, but I can't find a link to it. Maybe you have to be an adCenter user to get access....

Monday, November 13, 2006

Geek Babe Monday - Jessica Alba

Jessica Marie Alba was born April 28, 1981, in Pomona, California. She relocated with her family (her father was in the Air Force) several times; Biloxi, Mississippi, then back to California, followed by Del Rio, Texas, and finally back to the West Coast.

Alba knew she wanted to be an actress since the age of 5. Her first film role, in the 1994 film Camp Nowhere, came her way by chance. After one of the characters dropped out of the production, dark-haired Jessica was cast as the replacement, thanks to the fact that her hair matched that of the original actress. She then ventured onto the small screen, with commercials for Nintendo and J.C. Penney, and a recurring role in Nickelodeon's The Secret World of Alex Mack.

Following Nickelodeon, Jessica was cast as Maya in the 1995 series Flipper. While working with dolphins on the television series, Jessica was cast in the disastrous film Venus Rising, as well as the 1996 made-for-TV movie Too Soon for Jeff and an episode of Chicago Hope.

1998 was filled with television appearances, including a recurring role on the primetime soap Beverly Hills, 90210, Brooklyn South and The Love Boat: The Next Wave. As for film, audiences could catch Jessica in P.U.N.K.S. (aka Rebels), Never Been Kissed, and Idle Hands.

With Jessica's growing popularity and her exotic looks, it's no wonder that James Cameron cast her as the lead role in his television series, Dark Angel.
Jessica Alba in The Fantastic FourJessica Alba Swimsuit
Jessica returned to the scene with the title role in 2003's Honey. After her role as stripper Nancy Callahan in 2005's Sin City, Jessica starred in Into the Blue and Fantastic Four.

2007 promises to be a busy year. Jessica will be seen in a slew of movies: Saw IV, The Eye, Sin City 2, Fantastic Four: Rise of the Silver Surfer, Good Luck Chuck, Awake, Bill, and The Ten. Whew!

Friday, November 10, 2006

Counting Active Users in ASP

Although I have specifically addressed global.asa, I have written about it in the How to Filter out Bad Bots in ASP with Global.asa post. This method of keeping track of active users also uses the global.asa file along with an application variable. Put this in your global.asa:
<script language=vbScript runat=server>

sub session_onStart()
application("SCount") = application("SCount") + 1
end sub

sub session_onEnd()
application("SCount") = application("SCount") - 1
end sub

sub application_onStart()
' don't need a lock in onStart()
application("SCount") = 0
end sub
The, on any page you want to display the count, put
Response.Write "Users Online: application("SCount")
This method isn't 100% accurate and does have a few pitfalls:

It creates a "hotspot" during application.lock() that can cause delays when a new visitor arrives

You have no control over whether users launching new windows will trigger new sessions which will increment the count

You are constantly holding and modifying an application variable in memory, though you rarely use it

You need a modified copy of global.asa for each application, and have to aggregate different sites/applications separately

Nevertheless, for a quick and easy and mostly accurate way to count the number of active sessions, this will work.

Tuesday, November 07, 2006

Errors When Trying to do Response.Redirect

Ever tried to do Response.Redirect and get the following error?
Response object error 'ASP 0159: 80004005'
Header Error. The HTTP headers are already written to the client browser.
Any HTTP header modifications must be made before writing page content.
When calling response.redirect, you have to execute it BEFORE any client-side code, including the opening <html> tag.

This usually isn't a problem in IIS 6 (Server 2003) because in IIS 6 the default setting for Response.Buffer is True by default. This means the server holds the page content until it's all ready to go and then sends it to the client. If Response.Buffer is set to True, you can call Response.Redirect anytime before the page is complete and it will work.

In older versions of IIS, Response.Buffer is set to False by default. This means content gets sent to the client as the page is executing. In this scenario, you'll get an error if you try to execute Response.Redirect anytime after content has been sent.

The fix? If you're in a version of IIS older than IIS 6, set Response.Buffer = True at the top of the page.

Another error you can get when using Response.Redirect is Object Moved. One way to prevent this from happening is using Response.Clear first (note that buffering must be enabled). At the top of the page:
Response.Buffer = true
Before you call Response.Redirect:
Response.Redirect ""

Monday, November 06, 2006

Geek Babe Monday - Erin Gray

This is a late post, since I was a little under the weather yesterday and didn't make time to post. Missed last week too....

This post might more rightly be called Retro Geek Babe Monday. Erin Gray was one of the first models to successfully crossover into television. She is best known as "Kate Summers" on the highly watched TV show "Silver Spoons" (1982), and (the reason for her status as a Geek Babe) Colonel Wilma Deering on the TV show "Buck Rogers in the 25th Century" (1979).
Erin Gray - ModelErin Gray - Col Wilma Deering in Buck Rogers

Erin Gray was born on January 7, 1950 in Honolulu. Gray moved with her family from Hawaii to California when she was eight years old and graduated from Pacific Palisades High School. She was fifteen when a chance meeting with Nina Blanchard, head of one of Hollywood's top model agencies, convinced her what she wanted to do in life.

Moving to New York, she became one of the town's most sought-after models, in elite company with Farrah Fawcett, Veronica Hamel and Susan Blakely. TV viewers encountered her commercials for Breck, Max Factor, Clairol, Camay Soap and RC Cola, and a classic spot for English Leather cologne in which she provocatively delcared, "My men wear English Leather - or they wear nothing at all!"

Between modeling assignments, she studied acting with well-known coach Warren Robertson and when movie-TV offers came in, she was ready. Universal was impressed by her performances on such series as "Police Story" and "Gibbsville" and signed her to a seven-year contract. Under that pact, the studio co-starred her as a tough-minded newspaper reporter in Irwin Shaw'sEvening in Byzantium (1978) (TV).

Her performance scored with both critics and audiences, and led directly to the role in "Buck Rogers in the 25th Century" (1979). As a result, she has become a regular commuter between Hollywood and New York, the hub of the magazine and fashion world.

Tuesday, October 31, 2006

Using ASP to put the Current Date on a Page

Ever wanted to put the current date and or time on a page? ASP makes it easy using two built in functions:

Date() (or Now())

Date() returns the current date
Now() returns the current date and time

You can use either one with FormatDateTime().

FormatDateTime allows you to change the way the date/time appears.

FormatDateTime(date(),vbgeneraldate) = 10/31/2006
FormatDateTime(date(),vblongdate) = Tuesday, October 31, 2006
FormatDateTime(date(),vbshortdate) = 10/31/2006
FormatDateTime(now(),vblongtime) = 10:25:49 AM
FormatDateTime(now(),vbshorttime) = 10:25

You can use number values instread of the vb values, per the following chart:
vbGeneralDate0Display a date in format mm/dd/yy. If the date parameter is Now(), it will also return the time, after the date
vbLongDate1Display a date using the long date format: weekday, month day, year
vbShortDate2Display a date using the short date format: like the default (mm/dd/yy)
vbLongTime3Display a time using the time format: hh:mm:ss PM/AM
vbShortTime4Display a time using the 24-hour format: hh:mm

Friday, October 27, 2006

Internet FTP - Web Based FTP Clients

Ever needed to connect to an FTP site and not have access to an FTP client? And can't install a client? Or are you on a computer somewhere that has the FTP port is blocked?

The solution is Web based FTP - FTP over HTTP.

There are a few out there, but the most consistent and useful one I've found is Net2FTP.

At Net2FTP you can enter yout FTP server information and do all your FTP stuff through a web interface. So now you can work on your web sites no matter what computer you're on!

Thursday, October 26, 2006

How to Filter out Bad Bots in ASP with Global.asa

Bad bots? What are those? And why should I keep them out of my web site?

Glad you asked....

Do a search for "bad bot" and you'll find various lists, most written by web site owners who've noticed some strange behavior among the many bots visiting their sites. Although the The definition of a "bad bot" would vary depending on who you ask, there are various behaviors that can be/are considered bad:

not reading robots.txt
disobying robots.txt
requesting too many pages in a short time span
revisiting pages too often
e-mail harvesting
Guestbook spamming
Log Spamming
munging URL's
scraping content
and more....

Trying to use robots.txt for bad bots that either don't read robots.txt or disobey it won't work, so you'll have to use other methods against them. Although some bad bots change both their User Agent and IP address, using one or the other (or both) to ban these bots remains an easily implemented solution. If you want more advanced methods, do a search for "robot trap".

Using Gloabl.asa to Stop Bad Bots

Global.asa is an optional file that can contain declarations of objects, variables, and methods that can be accessed by every page in an ASP application. All valid browser scripts (JavaScript, VBScript, JScript, PerlScript, etc.) can be used within Global.asa.

The Global.asa file can contain only the following
  • Application events
  • Session events
  • <object> declarations
  • TypeLibrary declarations
  • the #include directive
Note: The Global.asa file must be stored in the root directory of the ASP application, and each application can only have one Global.asa file.

This isn't an artivle about Globals.asa, so I'm not going to go into depth here. Suffice to say that in Global.asa there's a section reserved for Session_OnStart. Session_OnStart occurs EVERY time a NEW visitor (including a bot) requests his or her (ot its) first page in the ASP application.

That means you can have a bit of code executed everytime a new visitors arrives on your site, no matter what the entry page.

Here's an example of a global.asa file that will direct bad bots, detected by user name, to a file named "badbot.html". The names here are actual bad bots I've found poking around my various sites. If you do a search for "bad bot lists", you'll find plenty of resources to help you identify bad bots.
<script language="vbscript" runat="server">
sub Session_OnStart
dim vUserAgent, vBad

vUserAgent = Request.ServerVariables("HTTP_USER_AGENT")
vBad = 0

Select Case vUserAgent
Case "larbin_2.6.3 larbin2.6.3@unspecified.mail"
Case "larbin_test nobody@airmail.etn"
Case "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; DTS agent"
Case "Missigua Locator 1.9"
Case " WebBot"
Case "<unknown user agent>"
Case "Web Downloader/6.5"
Case "Xenu Link Sleuth 1.2f"
Case "Zeus 46694 Webster Pro V2.9 Win32"
Case "LMQueueBot/0.1"
Case "Zeus 28879 Webster Pro V2.9 Win32"
Case "Offline Explorer/2.1"
Case "HTMLParser/1.4"
Case "Zeus 94377 Webster Pro V2.9 Win32"
Case "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; but first you must bring me a shrubbery)"
Case "W3CRobot/5.4.0 libwww/5.4.0"
Case "updated/0.1beta (;;"
Case "mogren_5.4.1"
Case "Holmes/1.0"
Case "Ken"
Case "Cuasarbot/0.9b"
Case "EmailSiphon"'2/22
Case "Java/1.4.1_04"
Case "OmniExplorer_Bot/1.09 (+ Rentals Crawler"
Case "BigCliqueBOT/1.03-dev (bigclicbot;;"
Case "combine/0.0"
Case "Avant Browser ("
Case "Anonymous/Password"
Case "Mozilla/4.0 pradipjadav@gmail"
Case "aipbot/1.0 (aipbot;;"
Case "abot/0.1 (abot;;"'4/8
Case "OmniExplorer_Bot/1.09 (+ Boats Crawler"
Case "AtlocalBot/1.1 +("
Case "Java/1.4.2_04"
Case "Web Downloader/6.3"
Case "sbSrer33n qeeyuSrSy hna"
Case "Java/1.4.2_06"
Case "MVAClient"
Case "4"
Case "versus crawler"
Case "telnet0.1"
Case "ssquidagent pradipjadav@gmail"
Case "Zeus 83206 Webster Pro V2.9 Win32"
Case "ichiro/1.0 ( 1 1 0.12%"
Case "larbin_2.6.3 larbin2.6.3@unspecified.mail"
Case "larbin_2.6.3 (larbin2.6.3@unspecified.mail)"
Case "MediaMirror (0.1a)"
Case "Missigua Locator 1.9"
Case "test/0.1"
Case "larbin_2.6.3 ("
Case "larbin_2.6.3"
Case "Java/1.5.0_02"
Case "noxtrumbot/1.0 ("
Case "versus crawler"
Case "updated/0.1beta (;;"
Case "0.1"
Case "Mozilla/4.0 (compatible; BorderManager 3.0)"
Case "Missigua Locator 1.9"
Case "BigCliqueBOT/1.03-dev (bigclicbot;;"
Case "POE-Component-Client-HTTP/0.65 (perl; N; POE; en; rv:0.650000)"
Case "RPT-HTTPClient/0.3-3"
Case "SeznamBot/1.0 ("
Case "Zeus 2339 Webster Pro V2.9 Win32"
Case "telnet0.1 ("
Case "Wget"
Case "Mozilla/4.0 (compatible; Cerberian Drtrs Version-3.2-Build-0)"
Case "HLoader"
Case "Java/1.4.1_04"
End Select

If vBad=1 then
End If
end sub
To use this, simply create a file named global.asa, paste in the above script, and save it in the root of your web site. Any bot with a user name matching any of the above will be sent to the non-existance web site "". You can use the same bad bots I'm using or build your own list.

Wednesday, October 25, 2006

The Importance of Fast Loading Web Pages

Usability studies have shown that visitors are willing to wait for about 10 seconds for a page to load before moving on. If your web page takes too long to load, you may consider redesigning it. Making the page load faster will increase the number of visitors that hang around and explore your site.

Despite the ready availability of broadband, high-speed connections still only make up about 60% of Internet connections. Not everyone has a cable modem, a T1, or DSL connection. The truth is that there are still a large number of surfers with modems of 56k.

Here are a few things you can do to make your pages load faster:

Consider taking out any embedded multimedia (background song, videos etc.) unless absolutely necessary. Yes, they might create a great effect, but if your visitors get impatient and leave, then the whole purpose is lost. Beside, most people don't want to be serenaded by the Andy Griffith theme while surfing your site.

Optimize images. You can do this with most image editing software. You can also save loading time by re-using images between pages (these will be on the browser's cache and will load much faster).

Preload images. If you have a few large images throughout your site, you can put it on the home page at the bottom of the page by simply adjusting the width and height to 1. This will make is seem to be no larger than a dot, even though the entire image will be loaded and saved to the disk cache.

Use height and width tags on your images. This way the browser will know were everything is before the images are loaded. The users can start to read what is on your site before all images are loaded.

If you are using tables, don't make very large ones, instead construct several smaller ones (you can always link them up if you want). This will help quite a bit. Also, don't put tables inside other tables if you can help it, because the browser will take longer to work out the spacing.

Remove "white space" (the spaces between your coding).

Last but not least, make sure your front page is as short as possible. A longer page will take a long time to load, even if it's all text. You can always use a link with extra info on another page. If you have too much content on a page, consider splitting it into two or more pages.

A fast-loading site will keep visitors waiting, and the last thing is you web visitors bailing out before the page loads becuase it's taking too long.

Monday, October 23, 2006

Geek Babe Monday - Jolene Blalock

Although Jolene proved to be a born surfer while growing up in California, she decided to pursue an acting career. Born and raised in California, Jolene's father would take her and her brothers to the water to surf, in what became a regular family sport.

She made her debut in 1998, in a bit part in the comedy Veronica's Closet, and The Love Boat: The Next Wave a year later.

Jolene got her big break when she was cast as Medea in the NBC miniseries, Jason and the Argonauts in 2000, co-starring Jason London and Natasha Henstridge.

2000 was a big year for the natural blonde turned brunette, who appeared in an episode of G vs E, a couple of episodes of D.C., and a guest spot on C.S.I.. She also appeared in the military drama JAG.
Jolene Blalock - Subcommander T'polJolene Blalock
Jolene hit the big time when she was cast as Subcommander T'Pol in the installment of the Star Trek franchise, Enterprise. With the success that Jeri Ryan brought to the show as Seven of Nine, Jolene is sure to make every man a Trekker. The part forced her to wear pointy devil ears, a wig and a catsuit (yes!), and incidentally, she was going to turn down the role at first.

All I have to say is "beam me up!"

Thursday, October 19, 2006

Changing the Way Links Look

Although CSS is pretty well know, I still often get or see questions about how to change the way links look and act. Using CSS, it's easy to change link appearance and behavior.

Here are the four CSS aspects of a link that you can affect:
A:link - base appearance of the link
A:visited - the way the link looks after it has been clicked on
A:hover - the way the link looks when the user hovers the mouse pointer over it
A:active - the way active links look; a link becomes active once you click on it

Example: remove underline and make links red on mouseover:

<STYLE TYPE="text/css">
a {text-decoration: none;}
a:hover {color:red;}
Note that by assigning value to the base 'a' tag, you change the appearance of ALL links. What if you only want to change some? Enter the subclass::

<STYLE TYPE="text/css">
a.LinkStyleOne:link {text-decoration: none;}
a.LinkStyleOne:hover {color:red;}
You would then contstruct the link like:
<a href="" class="LinkStyleOne">Link Text</a>

Using CSS you can change all sort of aspects of link appearance, highlight color, underline style, background color, size and much more.

Wednesday, October 18, 2006

How to Hide Email Addresses from Spammers

Are you tired of spam? I know I am.

Hard-core spammers use what are termed 'bot' to crawl the web and harvest email address. These bots search through page source code looking for email addresses. Because of the fixed format of an email address, they are pretty easy for to automatically extract from page code.

Here's a handy way to hide email address from most bots using JavaScript. Being client side executed, JavaScripts aren't run until the page is assembled by the client. Although most automated bots can see the JavaScript at the code level, they aren't able to execute it.

What this littel code does is seperate your email into variables, which JavaScript then assembles. Bot aren't able to assemble the parts, thus don't see the email address.
<script language=javascript>
var linktext = "Email the Webmaster";
var part1 = "webmaster";
var part2 = "";

document.write("<a href=" + "mail" + "to:" + part1 + "@" + par2 + ">" +
linktext + "</a>")

Replace 'Email the Webmaster' with whatever link text you want, 'webmaster' with the front part of your email address, and '' woth the rest of your email address. Notice that I also break up the 'mailto' portion as well.

Simply place this little script wherever you want. Users (with JavaScript enabled) will be able to see it, but the bots won't!

Tuesday, October 17, 2006

Write to a File in ASP

Here's a simple script that will create (or open) a file and write to it:
<% const ForAppending=8 const TristateFalse=0  dim oFile, oFSO, strFileName  'Create a filesystem object    Set oFSO = CreateObject("Scripting.FileSystemObject")  'The path and strFileName to write to strFileName="c:\inetpub\wwwroot\MyWebSite\files\testfile.txt"  'Open the file or create a new if it does not already exist Set oFile = oFSO.OpenTextFile(strFileName, ForAppending, true, TristateFalse)  'Write to the file oFile.Writeline "This is a test!"  'Close the file oFile.Close  'Free the objects set oFile=nothing set oFSO=nothing %>
If the file doesn't exist, it will be created and the line written to it. If the file already exists, it will be opened and the new line added at the bottom of all the existing lines.

If you are executing this script from a web page, then you need to make sure the the IUser account has been granted write permissions to whatever directory the file is in. In the example, you would need to make sure IUser has write permissions to the 'files' folder.

Monday, October 16, 2006

Geek Babe Monday - Alexa Davalos

Alexa Davalos starred opposite Vin Diesel in The Chronicles of Riddick adn was the character Gwen Raiden in the TV Series Angel. Born in 1982 in Paris, France, Davalos relocated to Los Angeles from New York. She made her first screen appearance in John Frankenheimer's Riviera when she was 3. Her love of performing took root when her family relocated to New York City and Davalos became involved with ballet. Following her mother (Elyssa Davalos, a notable television and theater actress) and grandfather (Richard Davalos, legendary for his performance as James Dean's brother in East of Eden) from set to set allowed Davalos a unique insight into the world of acting.
Alexa Davalos from Chroinicles of RiddickAlexa Davalos
Throughout her childhood, her mother continued training with Stella Adler. Davalos sat in on some of those classes and remembers the experience as having a profound impact on her desire to act. In her early teen years, she spent most of her time on location or in the theater with her mother, doing anything to be involved. It was also around this time that she made her first commercial with Patty Hansen. She has worked as a model with such prominent photographers as Peter Lindbergh and her father, Jeff Dunas.

ASP Database Connection Strings: Access and SQL Server

SQL Server 2000 ODBC connection using Named Pipes:
Driver={SQL Server};
SQL Server 2000 and 2005 ODBC connection via IP address (IP Address,Port - default port is 1433):
Data Source=,1433;
Network Library=DBMSSOCN;
Initial Catalog=DatabaseName;
User ID=Username;
SQL Server OLE DB:
Data Source=ServerName;
Initial Catalog=DatabaseName;
User Id=UserName;
SQL Server 2000 OLE DB via IP address (IP Address,Port - default port is 1433):
Data Source=,1433;
Network Library=DBMSSOCN;
Initial Catalog=DatabaseName;
User ID=UserName;
SQL Server 2005 ODBC connection using Named Pipes (You might need to download the SQL Server 2005 Native Client; the download contains both the ODBC and OLE DB providers):
Driver={SQL Native Client};
SQL Server 2005 Express ODBC connection to a database file (mdf):
Driver={SQL Native Client};
SQL Server 2005 Express ODBC connection when the file resides in the data directory:
Driver={SQL Native Client};
Access ODBC:
Driver={Microsoft Access Driver (*.mdb)};
Access OLE DB:
Data Source=\path\Database.mdb;
User Id=admin;

Friday, October 13, 2006

Sending Email in ASP Using CDONTS

I've already writen an article about sending email in Windows 2003 using CDO; no I'll present a method for sending email using ASP from Windows 2000.

In OS years, Windows 2000 is pretty ancient. Never-the-less, there are still plenty of web site hosted on Windows 2000 servers. On such a system, if you want to send mail from a web site, you need to use the CDONTS object as opposed to the CDO.

Here's the quick and easy for sending a text message
Dim MailBody
Dim objMail

MailBody = ""
MailBody = "This is an email from CDONTS." & vbCrLf
MailBody = MailBody & "It is easy to send mail using CDONTS" & vbCrLf
MailBody = MailBody & "Give it a try!"

Set objMail = CreateObject("CDONTS.NewMail")
objMail.From= ""
objMail.To= ""
objMail.Subject="Text Email from CDONTS"
objMail.Body= MailBody
set objMail=nothing
vbCrLf means Carriage return Line feed; this is to mark the end of a line and star a new one. The vb in fron mean - you guessed it - VBScipt. Ther are a bunch of other vb character codes available as well.

You can also send an strHTML formatted email with CDONTS:
Dim objMail
Dim strHTML

Set objMail = CreateObject("CDONTS.NewMail")
strHTML = ""
strHTML = strHTML & "<HTML>"
strHTML = strHTML & "<head>"
strHTML = strHTML & "<title>Sending CDONTS HTML Email</title>"
strHTML = strHTML & "</head>"
strHTML = strHTML & "<body bgcolor=""FFFFFF"">"
strHTML = strHTML & "<p><font size =""3"" face=""Arial""><strong>"
strHTML = strHTML & "It's also easy to send an HTML formatted email.</strong><br>"
strHTML = strHTML & "You can use HTML tags right in your string.</p>"
strHTML = strHTML & "<p align = ""center"">Beautiful HTML emails are only a momnet away!</p>"
strHTML = strHTML & "</body>"
strHTML = strHTML & "</HTML>"

objMail.From= ""
objMail.To= ""
objMail.Subject="HTML FOrmatted Email from CDONTS"

set objMail=nothing
There are a few things worth talking about here: Notice the double quotes inside the strHTML assignments. When assign attributes to tags, you have to use either:

Double Quotes: <font size=""3"">
Single Quotes: <font size='3'>
No Quotes: <font size=3>

If you use a single quote, the script will think that's where the string assignment ends and your script will break.

If you want to include an image, you can point to any image by fully qualifying the src tag: <img src="""">

If you send an HTML formatted mail to a mail reader that doesn't support HTML (most do), it will come out with the tags included.

With CDONTS, you can also send attachments and Carbon Copies. To send an attachment, use the AttachFile method. This method has three parameters:

1. Source, type String or Istream object. This parameter is required, and must contain the full path and file name of the attachment. Only C/C++ and Java programs can use an Istream object.

2. FileName (optional). This provides a file name to appear in the attachment's placeholder in the message. If not specified, the file name from the Source parameter is used.

3. EncodingMethod (optional). Indicates the encoding of the attachment. There are two possible values: 0, meaning the attachment is in UUEncode format; and 1, indicating the attachment is in Base64 format. (Base64 is the encoding scheme defined by MIME; UUEncode is an older format that you should use if you suspect your recipient(s) may not have a MIME-compliant system.) The default value of this parameter depends upon the MailFormat property. If the MailFormat property is set to 1, the default value of EncodingMethod is 0. If the MailFormat property is set to 0, the default value of EncodingMethod is 1.

Example of a completed AttachFile with all parameters:
objMail.AttachFile Server.MapPath("/DirectoryOnYourSite/TheFileToAttach.txt"),"FileYouAskedFor.txt",1

Here's the whole she-bang. IN this example, I want the file to be sent with the same name it has on my server, and since I'm setting MailFormat to 0, I dont' need to set the EncodingMethod parameter:
Dim MailBody
Dim objMail

Set objMail = CreateObject("CDONTS.NewMail")
objMail.From= ""
objMail.To= ""
objMail.Subject="CDONTS Email with CC and Attachment"

objMail.AttachFile Server.MapPath("/DirectoryOnYourSite/TheFileToAttach.txt")

MailBody = "CDONTS Mail with file attached" & vbCrLf
MailBody = MailBody & "The file must reside on your web server" & vbCrLf
MailBody = MailBody & "The File must be less than 2 megs in size" & vbCrLf
MailBody = MailBody & "If you need to send larger files, check with your hosting company" & vbCrLf

objMail.Body= MailBody
set objMail=nothing
More stuff to jam into your brain:

BodyFormat sets the text format of the mail object. It has two possible values: 0, which indicates that the body of the message includes Hypertext Markup Language (HTML); or 1, which indicates that the body of the message is plain text. The default value is 1, so this property does not need to be set for plain text messages.

MailFormat sets the encoding for the mail object. It has two possible values: 0, which indicates that the object is to be in MIME (Multipurpose Internet Mail Extension) format; or 1, which indicates that the object is to be in uninterrupted plain text. This property is optional, and its default value is 1. This property determines the default value for the EncodingMethod parameter in the AttachFile method (to be discussed later). When sending an attachment, set the MailFormat property to 0.

Importance (optional) sets the importance associated with the mail object. The possible values are: 0, indicating low importance; 1, indicating normal importance (default); and 2, indicating high importance. Format: objMail.Importance=2

The most common use of sending mail from a web page is having someone fill out a contact or some other form. In that case, you would wnat o have them enter their email address and message, which you could then grab from the form submission and assign in your Mail object.

Now go forth and spam not!

Thursday, October 12, 2006

Using LCase and UCase in ASP

These are pretty simple functions; nevertheless, in my traffic logs I see visitors showing up after conducting searches on how to change case in ASP... so here you go!

UCase changes everything to upper case:
will return
LCase() changes everything to lower case
will return
Here's a way to capitalize ONLY the first letter:
If you are converting to sentence case, you might want to put it in all lower case first, and then capitalize the first letter. Keep in mind that this doesn't account for proper names and other words that are normally capitalized: they will remain in lower case (supposing that's how they started out).

If you're not familiar with the Left() function I use above, here's another handy-dandy RetroWebDev post on string manipulation using Left, Right and Mid.

Wednesday, October 11, 2006

Using the Mod function in ASP to Create Dynamically Sized Tables

There's a relatively obscure function in ASP called Mod, which is short for "modular division" or "modulo" in Latin. The "modulus", or remainder, is what's left over when one number is divided by a number.

If you not as old as I am (or if you have kids right now) you might remember doing division with a remainder. So 13/6 = 2 R1, where R is the remainder (what's left over after you divide 12 by 6). Well that's what Mod is.

Syntax of the Mod function:
Number 1 mod Number 2
Here's a little loop to show you how the Mod function works to return the remainder:
For i = 1 to 10
Response.write(i & " mod 5 = " & i mod 5 & "<br>")
The results of this will look like:
1 mod 5 = 1
2 mod 5 = 2
3 mod 5 = 3
4 mod 5 = 4
5 mod 5 = 0
6 mod 5 = 1
7 mod 5 = 2
8 mod 5 = 3
9 mod 5 = 4
10 mod 5 = 0
Let's break down some of them:
1 mod 5 = 1 / 5 = 0 with a remainder of 1
2 mod 5 = 1 / 5 = 0 with a remainder of 2
6 mod 5 = 1 / 5 = 1 with a remainder of 1

In the commercial world, many advanced encryption techniques, including the world-standard RSA Encryption Algorithm, use the mod function as part of their internal functioning.

But what use is Mod to you?

The RetroWebDev post <a href=””>Alternating Row Colors</a> is a solution that makes use of Mod.

With a more advanced script, you can use Mod to create dynamically sized tables.

Say you want a table with 3 cells in each row, and there are 9 items to be displayed. Three rows of three and you're done. But suppose there are 10 items. To keep the table well-formed, you need to create three rows of three, then an additional row with one cell containing the 10th item, then two empty cells and a close row tag. Now what if you want to give the user the option of showing 3, 4, or 5 items per row? Mod to the rescue.

Here's a function that will produce a well-formed table based on your input of intCells and intItems. Simply pass in the values you want for number of cells per row and the total number of items:
Function MakeTable(intCells, intItems)

strHTML = "<table cellpadding=2 cellspacing=0>"

'Loop through the items
For i=1 to intItems
' start of new row?
If i mod intCells=1 Then strHTML = strHTML & "<tr>"

' Add a cell
strHTML = strHTML & "<td>" & i & "</td>"

' end of row?
If i mod intCells=0 Then strHTML = strHTML & "</tr>"

'if end of row, fill remainder with empty cells
If intItems mod intCells > 0 Then
' loop to complete table
For j=1 to intCells-(intItems mod intCells)
' add empty cell
strHTML = strHTML & "<td> </td>"

' close row if last cell
If j=intCells-(intItems mod intCells) Then strHTML = strHTML & "</tr>"
End if

' close table
strHTML = strHTML & "</table>"

' assign strHTML to the function
MakeTable = strHTML
End Function
Calling the function:
Call MakeTable(3,9)
creates a table with 3 cells per row and 9 total items.
Call MakeTable(5,21)
creates a table with 5 cells per row and 21 total items (4 rows of 5 and 1 row of 1 with 4 empties).

While you might not be in the cryptography business, if you create shopping carts, online stores, photo albums, calendars, etc., anything that might need the flexibility of tables with variable numbers of items and/or items per row, Mod is a great tool to be familiar with.

Tuesday, October 10, 2006

Server.Transfer versus Response.Redirect in ASP

When sending a user to another page, most programmers in ASP use Response.Redirect. Response.Redirect sends a message to the browser, telling it to go to another page:
Server.Transfer is similar in that it sends a user to another page, but it has some distinct advantages and disadvantages.

First, the big drawback of Server.Transfer is that it can only send users to a page running on that server. You can't use Server.Transfer to send a user to an external site. Only Response.Redirect can do that.

Transferring to another page using Server.Transfer conservers server resources. The transfer takes place on the server instead of forcing the browser to redirect to a new page. This means fewer HTTP requests coming through and can make your applications run more efficiently.

Server.Transfer maintains the the original URL in the browser.

Server.Transfer has a second parameter — "preserveForm". If you set this to True, using a statement such as Server.Transfer("WebForm2.aspx", True), the existing query string and any form variables will still be available to the page you are transferring to. This technique is great for wizard-style input forms split over multiple pages.

Don't confuse Server.Transfer with Server.Execute, which executes the page and returns the results.

When the Server.Transfer method is called, execution of the first page is terminated and execution of the second page begins. If the first page has started writing to the response buffer, the second page appends to the buffer instead of replacing it. If buffering is on, then HTTP headers can be modified by the ASP file that it is transferred to. If buffering is off, the HTTP headers are not modifiable by the ASP file that it is transferred to, unless no content has been sent by ASP yet. Additionally, multiple transfers can be called in succession, thereby chaining pages together.

The only data transferred to a second ASP page are the ASP built-in objects and the ASP Error object values from the first request. Any variables declared by the first ASP page are not available in the second ASP page.

Server.Transfer has several advantages over Response.Redirect:
  • Because it saves a round trip between the server and the browser it's faster and reduces the load on the Web server.
  • The Response querystring and form collections are preserved during the transfer. As a result, you don't need to worry about reposting form and querystring data to the new page.
However, Server.Transfer does have a few disadvantages:
  • You can only use Server.Transfer to redirect to a page on the same Web server.
  • You can't pass a querystring to the new page. (However, remember that the querystring passed to the page executing the transfer will automatically be passed along to the new page.) If you try to pass a querystring to the new page, you will trigger an ASP error.
  • The browser is never notified of the new page, which can cause problems with relative links in some cases.
Caution! The last point is more important than you think! I've found that this last point can be a show-stopper for using Server.Transfer. If you use Server.Transfer to transfer execution to page in a different folder, all of your relative links will break. This includes any relative links to images, anchor tags, or style sheets on the page.

Monday, October 09, 2006

Geek Babe Monday - Xenia Seeberg

Smart and hot!

Xenia Seeberg (born Anke Wesenberg on April 4, 1972 in Geldern , Germany) is a German film and television actress. She is perhaps best known for her role as Xev Bellringer in the science fiction television series LEXX.
Xenia Seeberg from LexxXenia Seeberg
Seeberg is 5ft 8in tall and speaks German, English, and French; and has degrees in Latin and philosophy. She attended theater school of Lee Strasberg in New York.

Improving ASP Performance Tip #1

Assign opbject variables to local variables.

Reading from the object variable is slower than reading from the local variable. So if you're going to be using the object variable frequently, store it in a local variable and access it that way.

If Object.Value = 0 then
Do something
elseif Object.Value > 0 then
Do something
elseif Object.Value
Dim vObject

vObject = Object.Value

if vObject = 0 then
Do something
elseif vObject > 0 then
Do something
elseif vObject <> etc...

Friday, October 06, 2006

Geek's Approach to working out Q&A

My post A Geek's Approach to Working Out generated some questions:

Q: Forty-one! That's pretty old....
A: You'll be there soon enough, if you're lucky.

Q: I don't believe you can total more than 1,000 lbs doing this workout
A: Meet in my local gym. For every pound over 1,000 I do, you pay me $5, for every pound under, I'll pay you $20.

Q: 15 reps sounds like too many. I always heard you need to do heavy weights and low reps to get bigger
A: You can certainly get bigger doing heavy weights and low reps, but no matter what weight or reps you're doing, you won't get bigger if you don't increase your tonnage (and as a result the amount of work your muscles are doing). You can't keep doing the same weight and same number of reps every workout if you want to get bigger and stronger.

And I offer this: find someone who can squat 150 pounds 15 times and find someone who can squat 250 pounds 15 times and see whose legs are bigger.

The whole key to getting bigger and stronger through resistance training is progression. To get bigger and/or stronger, you either need to:
  • keep adding weight
  • keep adding reps
  • reduce workout time
    With the goal being to increase your tonnage (the amount of weight you move). If you can both increase your tonnage and increase intensity (pounds moved per second) so much the better.

    Q: How can you get a good workout in only 20 minutes?
    A: How quickly you move the tonnage is the measure of intensity. You can increase your intensity without increasing weight or reps by doing your workout faster. Ultimately, you'll need to experiment to find the best combination of reps, weight, and intensity (pounds per second) for yourself.

    One of the best things you can do if you're serious about making gains is to keep a log. A log will let you see your progress as well as keep you on track. You can get a little notebook or do what I do and use a workout spreadsheet.
  • Passing Values out of a Subroutine in ASP

    It's a known fact that most programmers (and most people in general) are lazy. In ASP, declaring variables is optional, so most programmers don't. I've gotten away from that and tend to do Option Explicit at the top of my page and then declare all my variables
    Option Explicit

    Dim Var1, Var2, Var3
    I've found it makes it easier for me to debug my code.

    If you do declare your variables, do it first thing at the top of the page for your global variables. If you use function or subroutine variables, you can declare those inside the subroutine of function in which you use them.

    The main advantage of declaring variables is that it makes it easier to debug your code, because if you misspell a variable it will show up as undeclared. Declaring your variables has an added effect, usually unknown to beginning programmers.

    Say you have a subroutine the figures out the values of something:
    Sub DoSomething
    x = 1
    y = 2
    z = x + y
    End Sub
    Now you would think the value of z would be accessible to the rest of the page, but it won't unless you've declared it as a global variable outside the subroutine. So if you try this:
    Call DoSomething
    You won't see anything, because the value of z won't come out of the subroutine. You have to do it like this:
    Dim z

    Call DoSomething
    In this example, since z has been declared as a global variable, it will persist out of the function and be accessible to the rest of you code.

    I remember practically pulling my hair out the first time I used a subroutine to concatenate a string and then couldn't get the string to print!

    There is another way to accomplish this without using a script level Dim statement; you can declare the subroutine as Public.
    Public Sub DoSomething
    x = 1
    y = 2
    z = x + y
    End Sub
    So here are the rules summed up:
    • Variables in Private subroutines and functions are available only to that function or subroutine
    • Variables in Public subroutines and functions are available to the entire script
    • Variables declared at the script level are available to the entire script
    • Variables declared at the subroutein or function level are available only within that subroutine or function

    Thursday, October 05, 2006

    A Geek's Approach to Working Out

    Not being your typical geek (at least in my commitment to fitness and avoidance of Mountain Dew), I've been working out for more than 20 years. I'm no no power lifter, but even now, at the age of 41 and a bodyweight around 195 (6 feet tall), I can still exceed a 1,000 pound total on the three big lifts: squat, bench press, and dead lift.

    Over the years, my training has changed as I've learned new things and grown older. I take a much more cerebral approach to training now than I did when I was in my 20s and early 30s, where my main lifting philosophy was high weight, low reps, every set to failure and never go down. This type of max effort training eventually takes its toll, especially on joints like the elbows and shoulders and the lower back as well.

    As my strength gains trickled to a halt in my late 30s, I began exploring alternate ways of training and have come up with a good system that seems to work, in that I've made some very good progress using it over the last 8 months. It's more of a high intensity training approach, as opposed to the volume training I used to do (and the one most weight lifters still do).

    If you're a regular weight lifter, you probably do multiple exercises per body part, multiple sets per exercise, and reps in the range of 4 - 10. Typical weekly schedules for someone doing such a workout might be 4, 5 or 6 days a week, along the lines of:

    4 day
    Mon, Thurs:Chest, shoulders, triceps
    Tues, Fri:Legs, back, biceps

    5 day
    Mon, Thurs:Chest, triceps
    Wed:legs, shoulders
    Tues, Fri:back, biceps

    6 Day
    Mon, Thurs:Chest, triceps
    Tues, Fri:back, biceps
    Wed, Sat:legs, shoulders

    These volume workouts usually include doing 3-5 different exercises for each bodypart, 3-5 sets per exercise, and 4-10 reps per set, usually with the weight increasing each set. There's nothing wrong with such a workout - I did a workout much like this for almost two decades. Everyone can make good gains lifting this way, though you might often find yourself hitting a plateau now and then and needing to do something to break through. Varying the exercises you do each time, along with the amount of weight and the numbers of reps, is a good way to keep this routine from going stale.

    Since this type of routine didn't seem to be working for me that well, I decided to try something new. After much online research and experimentation, I came up with a routine that has resulted in good gains in size and strength over the last 8 months (bodyweight from 188 to 195 and all poudages heavier). It's based on several high intensity workout (HIT) methods, which you can read more about online if you've any desire to research it.

    To determine the intesity of my training (how much work I am doing), I calculate a pounds per second. Lets look at that in a volume workout. I'm going to use a few assumptions for the ease of calculation; YMMV:

    Time to complete 1 rep: 5 seconds
    Time between sets: 120 seconds
    Time between exercises: 180 seconds

    Here's some of the formulas I'm using:
    RepTime = Number of Reps * Number of Seconds to perform each Rep
    Set Rest Time = (Number of Sets - Number of Exercises) * Seconds of Rest Between Sets
    Exercise Rest Time = (Number of Exercises -1) * Seconds of Rest Between Exercises

    Tonnage is calculated on a per set basis:
    Tonnage = Reps x Weight

    I use Pounds per Second as representing 'intensity' of the workout:
    Pounds per Second = Total Tonnage/Total Time

    Now let's take a typical volume chest workout. Substitute your poundages for the ones below if you want to see how your workout breaks down. The time under each exercise include rest time between sets ((Number of Sets - 1)*120):

  • Bench Press: 135x10, 155x8, 185x6, 205x5
  • Time: 500 seconds; tonnage: 4520 pounds

  • Incline Press: 135x8, 135x8, 155x6, 175x4
  • Time: 490 seconds; tonnage: 3790 pounds

  • Decline Dumbell Press: 70x8, 70x6, 70x5
  • Time: 335 seconds; tonnage: 1330 pounds

  • Dumbell Flys: 45x8, 45x8, 45x6
  • Time: 350 seconds; tonnage: 990 pounds

  • Rest between exercises: 540 seconds
  • Total time: 1675 seconds
  • Total Tonnage: 10630 pounds
  • Pounds per second = Total Tonnage / Total Time = 6.35

    After chest, if you were doing the 4 day routine, you'd still have shoulders and triceps to do.

    I've included a spreadsheet breakdown of a full body workout done over two days. Change the weight used to see how your workout stacks up. Here's the final rollup for the Monday and Tuesday workouts of the 4 day a week routine listed above:
  • Total time: 13620 (3 hours 45 minutes)
  • Total tonnage: 53640
  • Pounds per second: 3.94

    With my new workout, I elminated almost all the isolation exercises and have gone to doing the full body three times a week (Mon, Wed, Fri). Each exercise is done for 1 set of 15 reps, with only 2 minutes of rest between exercises (keeping the assumed 5 seconds per rep). Here it is with the poundages I used on October 2, 2006:

    Mon, Wed, Fri
  • Squat: 205x15 - 75 seconds, 3075 pounds
  • Leg Curl: 130x15 - 75 seconds, 1950 pounds
  • Dumbell Press: 80x13 - 65 seconds, 1040 pounds
  • Lat Pulls: 160x15 - 75 seconds, 2400 pounds
  • Military Press: 105x15 - 75 seconds, 1575 pounds
  • Triceps Extensions: 100x15 - 75 seconds, 1500 pounds
  • Dumbell Curl: 40x12 - 60 seconds, 480 pounds

  • Total time: 1100 seconds (18mins 20secs)
  • Total tonnage: 12020
  • Pounds per second: 10.93

    Comparing the pounds per second here with that of the volume workout, we see that this routine moves almost 3x as much weight per second. Time taken to complete a full body workout has gone from 4 hours to just under 20 minutes (not including warmup and stretching). If you think this workout is for sissies, I challenge you to give it a try. Make sure you bring a barf bag the first few times!

    I realize a lot of traditional lifters will look at this and scoff, thinking that there's not enough work being done. Although overall exercises, sets, and tonnage is lower, there is actually more work being done per second of this workout - almost 4 times as much work.

    By cutting out isolation exercises and focusing on compound, multiple joint movements, we can get a full body, high intensity workout done in 20 minutes. I know a lot of you lifters enjoy those small isolation movements, but they really aren't necessary to a workout program with the goal of increasing OVERALL strength and fitness. If you're a bodybuilder, you might need to do isolation movements to bring out details, but for someone who simply wants to get stronger, bigger, and look better, basic, core strength movements are best. If you really want to put a little emphasis on a muscle group, add it in at the end as I've done with biceps and triceps.

    • Don't be a wuss! Go all the way doen on squats. Don't listen to whiners who say you'll injure your knees; that's BS. Knees are made to bend - deep squats wil make them stronger, not weaker.
    • Vary the exercises. For legs you might do squats on Monday, Leg Press on Wednesday, and Deadlifts on Friday. For chest you might do Flat Bench, Incline Bench, Dumbell press. For back maybe Close Grip pulls, Wide Grip Pulls, Seated rows.
    • Warm up! I do 20 minutes slow jog on a treadmill (5 miles per hour) at the start of every workout
    • Stretch! After the treadmill, I do 10 minutes stretching
    • Keep up the pace - don't slack between sets
    • You can do less reps with more weight if you want, but your goal should be to do no less than 10.
    • I do cardio 3x a week on my off days, jogging about 2.5 miles on Tues, Thurs and Sat or Sun
    Here's a spreadsheet of my current Geek Workout.

    Explanation of exercises you might not have done and how I do others:
    1. Power clean is gripping the bar slightly wider than shoulder width with your arms hanging in front. With knees slightly bent and keeping your elbows high, pull the bar up the front of your body and then rotate your arms underneath once the bar gets to your upper chest. From that position, press the bar straight up overhead. Then lower the bar to the upper chest, rotate the arms over the top of the bar, and lower it to the starting position. Focus on keeping the movements crisp and smooth. Use just enough leg to assist in getting the bar overhead.

    2. I do dumbbell curls seated and alternating arms, starting with my left (weaker) arm

    3. Narrow lat pulls are either gripping the bar underhanded with your hands close together, or using a v-bar. Hands should be no more than 6 inches apart.

    4. I do shrugs gripping the bar behind the bar, not in front. Easier on my lower back.

    Keep in mind you don't get that workout body through workouts alone. Watch your diet - you don't have to be a food Nazi, but you should do your best to limit fat and sugar. Get rid of soft drinks and fast food. Snack on trail mix or almonds or mixed nuts/dried fruit. Limit red meat, eat more chicken, preferable white meat chicken. Tuna is good, but don't eat too much because of mercury content. Replace white rice with brown, white bread with whole wheat. Eat more fruits and veggies. Make sure you get enough protein. If you're going for size gains, don't overdo tha cardio and make sure you're getting sufficient calories.

    It all might seeem overwhelming at first, but if you can get it going and stick with it for a few weeks, it will become habit and you'll soon be reaping the benefits.
  • Wednesday, October 04, 2006

    JavaScript Back Button or Link

    How do I make a back button or link?

    This is a pretty common (and basic) question with an easy solution. Note that it requires you visitor have JavaScript enabled, but that covers about 96% or more of web surfers.

    Link methods:
    <a href="javascript:history.go(-1);">back</a>
    <a href="#" onClick="javascript:history.go(-1);">back</a>
    <a href="javascript:;" onClick="javascript:history.go(-1)">back</a>
    Button method:
    <input type="button" value="Go Back" onClick="javascript:history.go(-1);">
    <img src="GoBackImage.jpg" onClick="javascript:history.go(-1);">
    You can change how many pages you sent the use back by changing the value in the parentheses; (-2) will send them back 2 pages.

    Tuesday, October 03, 2006

    Using the VBScript Replace() Function in ASP

    Ever wanted to search a string to find a word or phrase and replace it with a different word or phrase? ASP in VBScript provides an extremely useful and easy to use function - Replace() - that can be used replace all occurrences of a particular substring with a replacement substring.

    Here's the format:
    Replace(String, Find, ReplaceWith[, start[, count[, compare]]])
    String is the original string

    Find is the substring you are searching for

    ReplaceWith is what you want to replace the substring with.

    Find and replaceWith are the only required parameters.

    There are some optional parameters as well:
    start is the position in the original string where you want to start your seach from (default is 1)

    count is the number of occurances you want to replace (default is -1, which means all occurances)

    compare determines the mode to use when doing the comparison. vbBinaryCompare is case sensitive, vbTextCompare is not. By default, it is set to vbBinaryCompare

    The compare variable is worth looking at more in-depth. Example:
    Replace("quick brown fox", "Fox", "Dog")
    If you execute this, the result with be "quick brown fox". The replace won't happen because by using the default, vbBinaryCompare, "Fox" does not equal "fox": the case of the 'f' is different.

    If you want the replace to be case insensitive, you have to execute it as:
    Replace("quick brown fox", "Fox", "Dog", vbTextCompare)
    In this example, the result will be "quick brown Dog". It will ignore case when searching for "fox" and will replace it with the substring you specified, in this case "Dog" with a capital "D".

    Another way to do this is to use LCase() to make the string all lower case:
    Replace(LCase("quick brown FOX"), "fox", "DOG")
    The result here would be "quick brown DOG".

    Replace() is very useful for manipulating strings, but if you don't know how to set your case sensitivity, it can be frustrating!

    Monday, October 02, 2006

    Geek Babe Monday - Claudia Black

    Claudia Black portrayed Aeryn Sun on the SciFi series FarScape. She also showed up in Stargate SG-1 starting in 2006 as Vala Mal Doran
    Claudia Black FarScapeClaudia Black SG-1
    Claudia Black
    Claudia Black was born on October 11, 1972 (because she is very secretive about her personal life, her actual year of birth is still not officially certain), in Sydney, Australia. She has lived most of her adult life abroad, opting to reside in New Zealand and London, England, for extended periods of time.

    As an accomplished stage actress, Claudia has starred in many roles with various stage productions, as well as with the Belvoir Street Theatre, a renowned Australian acting troupe. In 1990, Claudia's turn as Portia in Shakespeare's The Merchant of Venice earned her a nomination in the Globe Shakespeare Competition; she made it to the finals.

    The bulk of her early onscreen career took place in Australian television productions. Early appearances include bit parts in G.P. (1993), Police Rescue (1993) and the miniseries Seven Deadly Sins (1993). She achieved notoriety in Australia by the mid-1990s, thanks to roles in A Country Practice (1994) and City Life (1996).

    Using Request.ServerVariables in ASP

    When a visitor requests a web page on your site, IIS gathers information from both the client computer and the server. This information is stored in a collection known as the Request.ServerVariables collection. ASP code has access to the collection, the information of which can be handy.

    With the ServerVariables, you can get the visitor's browser information, the referring URL, the visitor's IP address, the URL being fetched, and a bunch of other information.

    Here's a list of some of the more commonly used ones:

    • HTTP_USER_AGENT - the browser the visitor is using
    • LOGON_USER The Windows NT account that the user is logged into.
    • PATH_INFO Extra path information as given by the client. You can access scripts by using their virtual path and the PATH_INFO server variable. If this information comes from a URL it is decoded by the server before it is passed to the CGI script.
    • QUERY_STRING Query information stored in the string following the question mark (?) in the HTTP request.
    • REMOTE_ADDR - The IP address of the remote host making the request.
    • REMOTE_HOST - The name of the host making the request. If the server does not have this information, it will set REMOTE_ADDR and leave this empty.
    • SCRIPT_NAME - A virtual path to the script being executed. This is used for self-referencing URLs.
    • SERVER_NAME - The server's host name, DNS alias, or IP address as it would appear in self-referencing URLs.
    • URL - Gives the base portion of the URL.
    Here's a script you can run on an active server page to see the entire Request.Servervariables collection:
    For Each name In Request.ServerVariables 
    response.write(name&": ")
    On a presonal note, here's some of the things I've done using Request.Servervariable info:

  • Filter content by User Agent or IP
  • Ban based on IP
  • Ban bad bots based on User Agent
  • Build URL's based on Script or Server Name
  • Track access by IP

    Here's a cool app built around Request.Servervariables: a dynamic content tree to help your users navigate around your site.
  • Thursday, September 28, 2006

    Setting and Using Cookies in ASP

    Cookies are used to store information specific to a visitor to your website. A cookie is stored on the visitor's computer. You can set the expiration date of a cookie. If you set that date for sometime in teh future, the cookie will remain on teh visitor's comuter until that date, or until the visitor manually deletes it. If you don't set an expiration date, a cookie last for the duration of the session.

    Know that not everyone surfs with cookies on. Estimates are that about 6-10% of people surf with cookies off. If you relay on cookies, you're web site won't work right for these people. Cookies are a very common technology though and almost every site uses them. Still, you might want to check to see if your visitor has cookies enabled before you use them (which is a more complicated process than you might think).

    Creating an ASP cookie:
    Response.Cookies("FirstName") = "John"
    This example creates a cookie named FirstName that stores the value 'John'.

    Now that you've stored it, how do you read it? Easy:
    If you want, you can assign the value of the cookie to a variable so you can use it without having to call the request object each time
    Dim FName
    FName = ""
    FName = Request.Cookies("FirstName")
    If you want the cookie to last longer than the session, you can set an expiration date. Here's a cookie that lasts 10 days:

    Response.Cookies("FirstName") = "John"
    Response.Cookies("FirstName").Expires = Date() + 10
    You can also set a cookie to last until a specific date:
    Response.Cookies("FirstName") = "John" 
    Response.Cookies("FirstName").Expires = #December 31,2009#
    With the previous examples, I've only stored one value to the cookie. But cookies can hold more than one. You can make a cookie array or collection
    Response.Cookies("Name")("FirstName") = "John"
    Response.Cookies("Name")("MiddleName") = "Samuel"
    Response.Cookies("Name")("LastName") = "Smith"
    Response.Cookies("Name").Expires = Date() + 365
    You address an array or collection item just like you do a single cookie
    would show "John" on the screen.

    Now go bake some cookies!

    Tuesday, September 26, 2006

    SQL: Select Distinct with Most Recent Date

    I'm going to simplify it, but let's start here: suppose you have a database that keeps track of patients who show up to their doctor appointments. In that database, you have a table called AppointmentsKept; in that table you have two columns: Name and Date.

    Name = Patient Name
    ApptDate = Date of Appointment

    So if a patient has shown up to five appointments, his or her name will be in there five times with five different dates (one for each appointment). How can you get out each name only once, along with the most recent appointment date?

    If you try
    SELECT DISTINCT Name, ApptDate from AppointmentsKept ORDER BY AppDate DESC
    you end up getting each name listed multiple times, once for each date because each appointment date is different (distinct).

    Same things happens if you try
    SELECT Name, ApptDate from AppointmentsKept GROUP BY Name, ApptDate ORDER BY AppDate DESC
    What you need to do is reduce ApptDate down to a distinct value as well. Here's what I ended up with:
    FROM AppointmentsKept ORDER BY MAX(ApptDate) DESC
    The result is each name only once along with the most recent date for that name.

    Added 10/6/06
    A reader sent in another suggested method for selecting Distinct with the most recent date:
    select Name, ApptDate 
    from AppointmentsKept
    group by Name
    having AppDate = max(AppDate)

    Monday, September 25, 2006

    Geek Babe Monday

    Tricia Helfer is best know for her role as Number 6 on SciFi's Battlestar Galactica.
    Tricia Helfer Battlestar GalacticaTricia Helfer Battlestar Galactica
    Helfer was born in Stettler County, Alberta and raised on her family's grain farm. At the age of 17, while standing in line at a local movie theatre, she was discovered by modelling agency scout Kelly Streit. Her first leading role was portraying Farrah Fawcett in Behind the Camera: The Unauthorized Story of 'Charlie's Angels'. You can also see her in three upcoming movies: The Green Chain, Spiral, and The Genius Club.

    Sunday, September 24, 2006

    Manipulating Strings in Classic ASP with Left, Mid, and Right

    String manipulation is straightforward in classic ASP using the Left(), Mid(), and Right() functions.

    Left() returns the number of specified characters from the left side of a string. It's formatted as Left(string, length). Example:
    Left("one two three four five",3)
    would return "one" (the left 3 characters).

    Mid is a little more complex. It returns the number of characters specified from the starting point specified. It's formatted as Mid(string, start, length). Note that a length variable is optional; if omitted, Mid it will get the remaining character in the string from the starting point (just like a Right()). Example:
    Mid("one two three four five",4,3)
    would return "two" (start at character 4 and get the next 3 characters.

    Right() return the number of characters specified from the right side of the string. It's formatted as Right(string, length.)xample:
    Right("one two three four five",4)
    would return "five" (the right 4 characters).

    By using Left(), Mid(), and Right() in combination with Instr(), you should be able to get any peice of any string you need.

    Friday, September 22, 2006

    How to Send Email in ASP from Windows Server 2003

    Sending an email from a web page in ASP on a Windows 2003 server is easily accomplished. Be aware, however, that this method won't work on Windows NT/2000. In Windows Server 2003, mail is sent using CDOSYS. Windows NT/2000 uses CDONTS, but I'll talk about that in another article.

    Here's a simple Windows 2003 example that sends a text email. In this example, I constructed it as though I were grabbing information from a form submission:
    Set myMail=CreateObject("CDO.Message")
    myMail.Subject = "Sending email with CDO"
    myMail.From = ""
    myMail.To = ""
    myMail.Bcc = ""
    myMail.Cc = ""
    myMail.TextBody = "This is a message."
    set myMail=nothing
    You can also send HTML formatted emails:
    Set myMail=CreateObject("CDO.Message")
    myMail.Subject = "Sending email with CDO"
    myMail.From = ""
    myMail.To = ""
    myMail.HTMLBody = "<h1>This is a message.</h1>"
    set myMail = nothing
    You can send both a text version and an HTML versions
    Set myMail=CreateObject("CDO.Message")
    myMail.Subject ="Sending email with CDO"
    myMail.From =""
    myMail.To =""
    myMail.HTMLBody = "<h1>This is a message.</h1>"
    myMail.TextBody ="This is a message."
    set myMail=nothing
    You can include CCs and BCCs:
    Set myMail=CreateObject("CDO.Message")
    myMail.Subject = "Sending email with CDO"
    myMail.From = ""
    myMail.To = ""
    myMail.Bcc = ""
    myMail.Cc = ""
    myMail.TextBody = "This is a message."
    myMail.HTMLBody = "<h1>This is a message.</h1>"
    set myMail=nothing
    You can easily mail a web page:
    Set myMail=CreateObject("CDO.Message")
    myMail.Subject="Sending email with CDO"
    myMail.CreateMHTMLBody ""
    set myMail=nothing
    You can send an HTML file:
    Set myMail=CreateObject("CDO.Message")
    myMail.Subject="Sending email with CDO"
    myMail.CreateMHTMLBody "file://c:/inetpub/wwwroot/mywebsite/test.htm"
    set myMail=nothing
    And you can send a mail with an attachment
    Set myMail=CreateObject("CDO.Message")
    myMail.Subject="Sending email with CDO"
    myMail.TextBody="This is a message."
    myMail.AddAttachment "c:\inetpub\wwwroot\mywebsite\test.txt"
    set myMail=nothing
    It's not to difficult to have a form that passes the values to your mail object.

    Use a properly formatted return address. Many spam filters these days will automatically block emails that don't have a return address. Some block emails where the return address isn't a real address as well.

    Wednesday, September 20, 2006

    Black Hat SEO: How to get Inbound Links from Authority Sites

    In this article I'm going to talk about a black hat SEO method of getting inbound links from authority sites.

    If you decide to use the methods and techniques explained below, know that you could incur search engine penalties or even outright banning. I present this information for educational purtposes only, and to assist webmasters who are constructing sites and want to prevent this from happening to them.

    It's no secret that inbound liks exert a strong influence on a site's ranking. An inbound link from a well-respected authority site is very valuable, especially when such a site is an .edu or .gov. But getting links from these sites is next to impossible.

    Or is it?

    HTML injection is the technique of including HTML code when filling out a form. By so doing, it's sometimes possible to get the targeted site to do or display something 'out of the ordinary'. Using HTML injection techniques, unscrupulous individuals can insert code that results in the display of links to other sites, along with some key words.

    For the purposes of this example, I'll use a .edu site:
    This site uses a search application called WebGlimpse. Glimpse stands for GLobal IMPicit SEarch and is an indexing and query application. At the bottom of the page, you can see the form for entering a search into their WebGlimpse application. While newer versions of Glimpse have been updated to account for HTML injection, there are plenty of sites out there still using older verions.

    Here's where HTML injection comes in. Cut and paste the following into the search box, execute the search, and see what happens:
    If you URL decode this you get:
    What you end up with on the results page is a link to, along with whatever keywords you appended to the end of the search string. Look in the address bar; you'll see that the address contains the search string you just typed in (along with some other stuff):
    Trimming the variables results in:
    Cut and past this into your browser and you get the search results page directly. So, in theory, you could put this out as a link somewhere, search engine spiders would follow it to the WebGlimpse page, then follow that link to... well, wherever you had it pointing.

    Many times the WebGlimpse search isn't readily accessible. In that case, you'll need to play detective to figure out how to build the URL.

    Here's an URL decoder/encoder.

    Remember - he (or she) who flys above the radar gets shot down.

    Tuesday, September 19, 2006

    Removing SQL Server Full Text Noise Words from a Search String

    Remove SQL Full Text Noise Words

    I'm not going to get into the specifics on constructing full text queries (at least not yet). In this article I'm going to tell you the full text noise words and provide a function for stripping them from queries so you don't get an error.

    Have you ever gotten this error when executing a search against your full text index:
    Microsoft OLE DB Provider for SQL Server error '80040e14' 

    Execution of a full-text operation failed. A clause of the query contained only ignored words.
    SQL Server Full Text Indexing provides support for sophisticated word and phrase searches. The full text index stores information about words and their location within a given column, This information is used to quickly complete full text queries that search for rows with particular words or combinations of words.

    The noise words (assuming you did a standard install of SQL Server 2000) are located at:
    \Program Files\Microsoft SQL Server\MSSQL\FTDATA\SQLServer\Config
    In that directory, you'll see a bunch of files named noise with different extensions. The extension indicates the language. The English noise files is noise.eng. If you open that file with a text editor, you can see all the noise words (and symbols, like the $ for example). A search containing any of these words or symbols will cause an error.

    There are a couple of options here for preventing that error. One is to remove the noise word from the noise file, then rebuild your full text index. I did this for a site where I aggregated video games for price comparison. I needed visitors to be able to search on Playstation 2. Well, the number 2 is in the noise file, so I took it out.

    Even if you decide to take some words out of the noise file, you'll still need to prep your search string by removing any noise words that are left. The easiest way to do that is a function. When someone does a search, pass their search phrase into the function and then return the phrase with the noise words stripped out. There are several ways to do this, but here's the method I went with:
    Function PrepSearchString(sOriginalQuery)
    strNoiseWords = "1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | $ | ! | @ | # | $ | % | ^ | & | * | ( | ) | - | _ | + |
    = | [ | ] | { | } | about | after | all | also | an | and
    | another | any | are | as | at | be | because | been | before | being | between |
    both | but | by | came | can | come | could | did | do | does | each | else | for |
    from | get | got | has | had | he | have | her | here | him | himself | his | how |
    if | in | into | is | it | its | just | like | make | many | me | might | more |
    most | much | must | my | never | now | of | on | only | or | other | our | out |
    over | re | said | same | see | should | since | so | some | still | such | take |
    than | that | the | their | them | then | there | these | they | this | those |
    through | to | too | under | up | use | very | want | was | way | we | well | were |
    what | when | where | which | while | who | will | with | would | you | your | a |
    b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v |
    w | x | y | z"

    arrNoiseWord = Split(strNoiseWords,"|")

    For i = 0 to UBound(arrNoiseWord)
    sOriginalQuery = " "&LCase(sOriginalQuery)&" "
    sOriginalQuery = Replace(sOriginalQuery," "&Trim(arrNoiseWord(i))&" "," ")
    PrepSearchString = Trim(sOriginalQuery)
    End Function
    How it works:

  • I took all the noise words and put them in one long pipe delimited string
  • Split the string to make an array
  • Loop through the array looking for noise words
  • When I find one, replace it with a space

    Some other considerations:

    In the function above, I added an extra space before and after the pipe. This was to allow the long string to break and fit onto the page. To account for that, I use the Trim command referring to an item in the array. If you build the string without the spaces, you won't need the Trim (though it won't hurt anything).

    I took the original query and put a space on either end. This is to account for a noise word being at either the beginning or the end of the search phrase.

    I also use LCase to put the original query into all lower case. I could do a vbTextCompare, but it was easier for just make the term all lower case.

    Lastly, I trim the original query when assigning it to go back to account for a space being at either end (when the noise word started or ended the query)

    Once you have the noise word free search string, you can build your query. Be aware: if the search query contains noise words only, this returns an empty string. You should for that in your code.
  • Monday, September 18, 2006

    Geek Babe Monday

    Sometimes programming can be so.... boring, so I thought I'd liven up RetroWebDev with a new feature: Geek Babe Monday.

    Every Monday, I'll post a Hot Geek Babe. Don't be shy if you have suggestions for future features!
    Kristanna Loken Terminator 2Kristanna Loken Terminator 2

    Kristanna Loken from Terminator 3, the ScFi Movie Dark Kingdom, BloodRayne, and the upcoming In the Name of the King: A Dungeon Seige Tale (due out in 2007).

    Sunday, September 17, 2006

    ASP Function to Capitalize the First Letter of All Words in a Sentence

    Here's a function you can use that will capitalize the first letter of all words in a sentence. Simply pass in the text string containing the words you want capitalized.

    You send in: "The quick brown fox"
    You get out: "The Quick Brown Fox"

    First call the function:
    YourString="The quick brown fox"
    strCaps = Capitalize(YourString)
    The Function:
    Function Capitalize(X)
    'return a string with the first letter of the word capitalised
    If IsNull(X) Then
    Exit Function
    lowercaseSTR = CStr(LCase(X))
    OldC = " "
    MyArray = Split(lowercaseSTR," ")
    For IntI = LBound(MyArray) To UBound(MyArray)
    For I = 1 To Len(MyArray(IntI))
    If Len(MyArray(IntI)) = 1 Then
    newString = newString & UCase(MyArray(IntI)) & " "
    ElseIf I=1 Then
    newString = newString & UCase(Mid(MyArray(IntI), I, 1))
    ElseIf I = Len(MyArray(IntI)) Then
    newString = newString & Mid(MyArray(IntI), I, 1) & " "
    newString = newString & Mid(MyArray(IntI), I, 1)
    End If
    Next 'IntI
    Capitalize = Trim(newString)
    End If
    End Function

    Tuesday, September 12, 2006

    Archive Calendar for Blogger Blogs

    After I'd gotten numerous posts in my RetroWebDev blog, I noticed that it can be difficult to locate a post after clicking on one of the monthly links under the Archive section.

    It didn't take much research to find a handly script that generates an Archive Calendar for blogger. To see it in action, click on one of the monthly links uder the Archive section on the left.

    With the Archive Calendar, you can click right a specific day to see the post(s) on that day. I like.

    Mucho thanks to whomever created this script.

    ASP Includes Explained

    Include files are external files that you can easily add to any page:
    It's important to note that include files are processed and inserted BEFORE scripts on the page are run. Included files can contain functions, sub-routines, navigational elements, or just about anything else. Include files are at the heart of making efficient ASP based sites, as they allow you to put you code into easily re-usable chunks.

    There are two forms of the include statement: Virtual and File:

    By using Virtual, you can include any file no matter what its location because Virtual starts as the root directory of the site. In contrast, the File statement starts in the CURRENT directory (the same directory as the file containing the Include).

    Example #1
    could include a file from the AAA directory, even if the page with this statement is in the /BBB/ folder.

    Example #2:
    would fail when used in any file not located in the AAA directory.

    Example #3:
    will succeed from any directory on the same level as the AAA directory. It's instructing the include to go up one directory and then into the AAA directory to get the file. However, if the page that contains the include is moved to a different level in the directory structure, the Include statement would fail.

    I almost always use the Virtual format. That way I know I'm starting at the site root. Even if I move the page containing the include to a different directory, it will still work.

    Sunday, September 10, 2006

    SQL to get Records for Last 24 Hours using DateAdd

    One of the applications I wrote is a logging system. One of the requested modifications is that I make it easy for managers to look at entries made in the last 24 hours. Right now, I have hard-coded links for Current Day, Yesterday, Last 7, etc.

    Current Day shows entries made since midnight. Yesterday shows entries made for the previous day. So right now there's no quick way to get the entries for the last 24 hours.

    The solution was simple:
    Select * from Entries where DateAdded >= " & DateAdd("d",-1, Now())
    How it works:

    The VBScript Now() function gets the current date and time. I then use the DateAdd funciton to subtract 1 day. I then get all entries with a DateAdded timestamp great than or equal to resulting date and time.

    Alternately, you could subtract hours:
    Select * from Entries where DateAdded >= " & DateAdd("h",-24, Now())

    DateAdd is pretty nifty.

    DateAdd(datepart, number, date)

    Datepart can be (abbreviation):
    year (yyyy)
    quarter (q)
    month (m)
    day (d)
    week (ww)
    hour (h)
    minute (n)
    second (s)

    1 month from today: DateAdd("m", 1, Now())
    100 years ago: DateAdd("yyyy", -100, Now())
    10 minutes from now: DateAdd("m", 10, Now())
    1 quarter (3 months) ago: DateAdd("q", -1, Now())

    Friday, September 08, 2006

    WWW Versus Non-WWW URLs: Dupe Content and Redirecting

    I'm not real clear on the technical reasons behind it all, but the consensus in the SEO community is that it's detrimental because of duplicate content penalities to have your web site operating on both the www and non-www addresses. Ideally, you should pick one address or the other and have visitors (human and otherwise) who come to the other forwarded using a permanent redirect. So if you choose as your web web address, you should have all visitors to forwarded to

    This is easy enough to do for one page, but how about some handy code that will automatically forward all your pages using a search engine friendly 301 redirect?

    This code that will forward any visitor to the www version of your site, no matter what the entry page. Place this code at the top of every page:

    Dim strDomain, strURL, strQueryString, strHTTPPath,vTempNum

    'Get page domain
    strDomain = LCase(request.ServerVariables("HTTP_HOST"))

    'Check for www
    If Left(strDomain, 3) <> "www" Then
    strHTTPPath = Request.ServerVariables("PATH_INFO")

    'If page is default.asp, send to root
    If right(strHTTPPath, 12) = "/default.asp" Then
    vTempNum = Len(strHTTPPath)-11
    strHTTPPath = Left(strHTTPPath,vTempNum)
    End If

    'If page is index.asp, send to root
    If right(strHTTPPath, 10) = "/index.asp" Then
    vTempNum = Len(strHTTPPath)-9
    strHTTPPath = Left(strHTTPPath,vTempNum)
    End If

    'Set the new URL
    strQueryString = Request.ServerVariables("QUERY_STRING")
    strURL = "http://www." & strDomain & strHTTPPath

    'If any, pass on query string variables
    If len(strQueryString) > 0 Then strURL = strURL & "?" & strQueryString

    '301 Redirect to www version
    Response.Status = "301 Moved Permanently"
    Response.AddHeader "Location", strURL
    End If
    Likewise, here is code that will redirect to the non-www version:

    Dim strDomain, strURL, strQueryString, strHTTPPath,vTempNum

    'Get Page domain
    strDomain = lcase(request.ServerVariables("HTTP_HOST"))

    'Check for www
    If Left(strDomain, 3) = "www" Then
    'Change to non-www version
    vTempNum = Len(strDomain)-4
    strDomain = Right(strDomain,vTempNum)
    strHTTPPath = Request.ServerVariables("PATH_INFO")

    'If page is default.asp, send to root
    If right(strHTTPPath, 12) = "/default.asp" Then
    vTempNum = Len(strHTTPPath)-11
    strHTTPPath = Left(strHTTPPath,vTempNum)
    End If

    'If page is index.asp, send to root
    If right(strHTTPPath, 10) = "/index.asp" Then
    vTempNum = Len(strHTTPPath)-9
    strHTTPPath = Left(strHTTPPath,vTempNum)
    End If

    'Set new URL
    strQueryString = Request.ServerVariables("QUERY_STRING")
    strURL = "http://" & strDomain & strHTTPPath

    'If any, pass on query string variables
    if len(strQueryString) > 0 Then strURL = strURL & "?" & strQueryString

    '301 redirect to non-www version
    Response.Status = "301 Moved Permanently"
    Response.AddHeader "Location", strURL
    End If

    Thursday, September 07, 2006

    Scraping Search Engines for Page Content

    It's no secret that optimized page content can help you rank better in organic search engine results. While page content is not the deciding factor, it's stil important, especially for long tail search terms. No matter what your page content, you're probably not going to be able to rank well for the term 'dvd player' (or any other exceptionally competetive term). But you might be able to rank on a long tail term such as 'panasonic portable dvd player model 123ABCxyz'. For such a long tail phrase, optimized page content can help.

    But how to get the content? Well, you could write it. Or buy it. Or... scrape it from somewhere else.

    If you chose the scraping solution, I highly recommend that you have some original content on the page. Write a capsule review, or your own description, or anything else that likely doesn't exist on some other site (until, of course, someone scrapes it).

    The following process will step you through scraping content from a search engine and adding it to your page.

    WARNING: Implementing these techniques could very well get you banned from the search engines!

    1. Decide on the keyword you want to generate content for. For this example, I'll use the fictitious Panasonic DVD model from above.

    2. Decide how many search engines you're going to scrape content from. For this example, I'll use 3: MSN, Yahoo, and Gigablast.

    3. Since I'm using 3 search engines, I generate a random number between 1 and 3.

    4. Now pass the number into a page scraping function to go out and get the search results for that phrase from the search engine. The way you might do it is pass the number and keyword into the function:
    strContent = ScrapeContent(intRandNum,"panasonic portable dvd player model 123ABCxyz')
    Then, in the function, have a Case statement to assign the url based on the random number:
    Function ScrapeContent(TheEngine, TheTerm)
    Select Case TheEngine
    Case 1
    URL = ""&TheTerm&""
    Case 2
    URL = ""&TheTerm"&"
    Case 3
    URL = ""&TheTerm&""
    End Select

    Set xmlObj = Server.CreateObject("MSXML2.ServerXMLHTTP")

    xmlObj.Open "GET", url, true
    Call xmlObj.Send()

    On Error Resume Next

    If xmlObj.readyState <> 4 Then xml.waitForResponse 3

    If Err.Number <> 0 Then
    ScrapeContent = "There was an error retreiving the remote page"
    If (xmlObj.readyState <> 4) Or (xml.Status <> 200) Then
    ScrapeContent = "Problem communicating with remote server..."
    ScrapeContent = xmlObj.ResponseText
    End If
    End If
    End Function
    5. In the above example. we know have the remote content assigned to a variable named strContent. Parse that string to strip out eveything between the body tags:
    strContent = Mid(strContent,Instr(strContent,"<body>")+6,Instr(strContent,"</body>"))
    6. Now that you have the body content, replace all the breaks with a space; this is because words might run to gether otherwise:
    strContent = Replace(strContent,"<br>"," ")
    7. There are some extraneous words that have no relevancy to your search term that you're probably going to want to strip out as well. For example, Yahoo has additional links that follow the listing: 'Cached', 'More from this site', 'Save' etc. I recommend customizing a function to strip out all the non keyword related carp that clutters the search results you just scraped.

    8. Now you have fairly clean copy relevant to your keyword, the last thing to do strip out the HTML.

    The end result: a big chunk of text directly relevant to your keyword.

    There's a few things you can do with it: plop it right on the page (not recommended), hide it in a div, or - my recommendation - hide it in a div and reverse cloak it, so that only search engines can see it. Hopefully your pages will start climbing higher in the organic results.

    Leverage those favorable results quickly! Sooner or later you'll find your site banned, either because the spiders got smarter or a competitor reported you.

    Good luck!