www.geekybob.com

Just a short, simple blog for Bob to share his thoughts.

RFC 7151 - File Transfer Protocol HOST Command for Virtual Hosts

14 March 2014 • by Bob • IIS, FTP, SSL, HOST

I received an email yesterday from the RFC Editor that a new Request for Comments (RFC) document has just been published, RFC 7151, which adds support for a new "HOST" command to FTP. This new command allows hosting multiple FTP sites on a single IP address, much like what Host Headers provide for HTTP.

Here's the URL to the new RFC on the RFC Editor website:

File Transfer Protocol HOST Command for Virtual Hosts
http://www.ietf.org/rfc/rfc7151.txt

Or you can see the HTML-based version at the following URL:

http://tools.ietf.org/html/rfc7151

One minor point which I would like to clarify is that this adds a new command for FTP to specify which virtual host to connect to. I periodically hear people referring to this as "FTP Host Headers", but that is technically incorrect since FTP does not have request headers like HTTP. Here's a simple example of what the communications flow looks like when the HOST command is used:

CLIENT> HOST ftp.example.com
SERVER> 220 Host accepted
CLIENT> USER foo
SERVER> 331 Password required
CLIENT> PASS bar
SERVER> 230 User logged in

I need to make sure that I thank my co-author for this RFC, Paul Hethmon, who has authored other FTP-related RFCs over the years. For example, Paul wrote RFC 3659, and he co-wrote RFC 2389 with Robert Elz. As a result, the Internet community has Paul and Robert to thank for several great FTP command extensions in the past. (e.g. FEAT, OPTS, MDTM, SIZE, REST, MLST, MLSD, etc.) Paul and I co-wrote RFC 7151 over the past several years, and it was great working with him.

Support for the HOST command has been built-in to Microsoft's FTP service since IIS 7.0, but now that the RFC has been officially published I hope that this feature will be adopted by other FTP servers and clients. That being said, IIS is not the only implementation of the FTP HOST command; at the time of this blog post, these are the server and client implementations that I am aware of which already provide support for this new command. (Note: there may be more than I have listed here; these are just the implementations that I currently know about.)

In addition to the clients listed above, if you have been reading my series on FTP clients over the past few years, I have posted details on how to use the FTP HOST command with some other FTP clients which do not provide built-in support. For example, the Core FTP Client allows you to specific pre-login commands as part of an FTP site's connection properties, so you can manually type in the HOST command and save it along the site's settings.

A Little Bit of History

When I joined the feature team which was creating the FTP service for Windows Server 2008, one of the things that bothered us was that there was no way at the protocol level to host multiple FTP sites on the same IP address. There were several ways that FTP server implementations were approximating that sort of functionality, for example the User Isolation features that we ship with FTP for IIS, but each FTP server seemed to be implementing its own workaround and there was no standardization.

Because of this limitation, our team received a lot of requests to add "FTP Host Headers," although as I explained earlier FTP has no concept of request headers. To help address some of the questions which I was often seeing, I explained the lack of hostname support for FTP in detail in the comments section of my FTP User Isolation with Multiple User Accounts blog that I posted back in 2006, which was shortly before we began work on implementing the HOST command. I will paraphrase some of my comments here:

While I realize that the ability host multiple FTP sites on the same IP address and port like HTTP is a desired configuration, the simple answer is that FTP does not currently support this at the protocol level. To put things in perspective, RFC 959 is the governing document for FTP, and that was published in October of 1985. FTP was simply not designed for the Internet as we use and understand it today, even though it is a generally reliable protocol that many people will continue to use for some time. HTTP/1.1 was designed much later and resolved this problem, but only for HTTP requests.

There are three ways that you can create unique bindings for a web or HTTP site: IP address, port, or host header. FTP can create unique bindings by IP address or port, but the FTP protocol does not currently provide support for hostnames.

Here's why: HTTP packets consist of a set of request headers and possibly a block of data. Here's an example of a simple GET request:

GET /default.aspx HTTP/1.0 [CrLf]
Accept: */* [CrLf]
[CrLf]

When HTTP 1.1 was published in RFC 2068 and RFC 2616, it defined a header for specifying a "host" name in a separate name/value pair:

GET /default.aspx HTTP/1.1 [CrLf]
Host: example.com [CrLf]
Accept: */* [CrLf]
[CrLf]

The "Host" header allows multiple HTTP virtual servers ("hosts") on the same IP address and port that are differentiated by host name. While this works great for the HTTP protocol, FTP currently has no comparable functionality. As such, the FTP protocol would have to be updated to allow multiple hosts on the same IP address and port, then FTP servers and clients would need to be updated to accommodate the changes to FTP.

While my explanation may have clarified root cause of the FTP limitation for anyone who was asking about it, I personally thought the situation was unacceptable. This inspired me to research the addition of a new command for FTP which would allow FTP clients to specify hostnames. As I was researching how to propose a new RFC document to the IETF, I discovered that Paul Hethmon had been researching the same problem a few years earlier. I contacted Paul and offered to combine our work, and he agreed. After several years of work and a great deal of supportive assistance from dozens of great people whom I met through the IETF, RFC 7151 has finally been published.

There are a lot of people besides Paul whom I should thank, and we mention them in the acknowledgments section of our RFC, which you can read at the following URL:

http://tools.ietf.org/html/rfc7151#appendix-B

One final note - two of my coworkers, Jaroslav Dunajsky and Wade Hilmo, are mentioned in the acknowledgments section of the RFC. Jaroslav is the developer who implemented the FTP HOST command for IIS, and Wade is a senior developer on the IIS team who graciously allowed me to bounce ideas off him while I was doing my research over the past few years. (I probably I owe him a lunch or two.)


Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

FTP Clients - Part 13: WinSCP

28 February 2014 • by Bob • FTP, SSL

For this next installment in my series about FTP clients, I want to take a look at WinSCP, which is an open source FTP/SFTP client that is available from the following URL:

http://www.winscp.net/

For this blog post I used WinSCP 5.5.1, and it was available for free when I wrote this blog post. That being said, WinSCP's author (Martin Prikryl) takes donations. (And I think that it's a worthy cause; I like to support independent development work.)

WinSCP 5.5 Overview

When you open WinSCP 5.5, you will first see the Login dialog box, which will be empty until you add some sites to the list. The Login dialog allows you to create folders so you can categorize your sites, and the user interface is comparable to what you would expect in a Site Manager for other FTP clients.

Fig. 1 - The opening Login dialog in WinSCP 5.5.

When you are adding FTP sites, you have three choices for the protocol: FTP, SCP, and SFTP; you also have four choices for encryption: No encryption, TLS/SSL Implicit encryption, TLS Explicit encryption, and SSL Explicit encryption. (I'll discuss those later.)

When you open a site for which you did not save the password, (which I highly recommend), you will be prompted for your password.

Fig. 2 - The WinSCP 5.5 Password dialog.

Once your FTP site is opened, the main application window is displayed, and it resembles a two-column file explorer interface with local and remote folders, which you might expect from a GUI-based FTP client. (Note: WinSCP refers to this as it's "Commander" interface.)

Fig. 3 - Local and Remote Folders.

That being said, if you change your application preferences, you can change the user interface so that it uses a single-column file explorer interface with a folder tree, which might be useful if you would rather use the FTP client as a drag-and-drop repository. (Note: WinSCP refers to this as it's "Explorer" interface.)

Fig. 4 - Remote Folder Tree and Files.

WinSCP 5.5 has support for automation through .NET and COM, and documentation about automating WinSCP 5.5 programmatically is available on the WinSCP website at the following URL:

WinSCP .NET Assembly and COM Library

There are several detailed automation examples on the WinSCP website that are written in C#, VB.NET, PowerShell, JavaScript, VBScript, etc., and the documentation is quite good. If you need to do a lot of FTP scripting and you are looking for a good way to automate your FTP sessions, you might want to consider this FTP client.

If you don't want to write a bunch of code, you can also automate WinSCP from a command line, and the documentation about that is available on the WinSCP website at the following URL:

WinSCP Command-line Options

Another great feature about WinSCP is that it can be downloaded as portable executables, which makes it easy to copy between systems. This is a great feature for me since I like to keep a collection of handy utilities in my SkyDrive/OneDrive folders.

Using WinSCP 5.5 with FTP over SSL (FTPS)

WinSCP 5.5 has built-in support for FTP over SSL (FTPS), and it supports both Explicit and Implicit FTPS. To specify which type of encryption to use for FTPS, you need to choose the appropriate option from the Encryption drop-down menu for an FTP site.

Fig. 5 - Specifying the FTPS encryption.

Once you have established an FTPS connection through WinSCP 5.5, the user experience is the same as it is for a standard FTP connection. That being said, I could not find a way to drop out of FTPS once a connection is established, so FTPS is an all or nothing option for your sessions.

Using Using WinSCP 5.5 with True FTP Hosts

True FTP hosts are not supported natively, and even though WinSCP 5.5 allows you to send post-login commands after an FTP site has been opened, I could not find a way to send a custom command before sending user credentials, so true FTP hosts cannot be used.

Using Using WinSCP 5.5 with Virtual FTP Hosts

WinSCP 5.5's login settings allow you to specify the virtual host name as part of the user credentials by using syntax like "ftp.example.com|username" or "ftp.example.com\username", so you can use virtual FTP hosts with WinSCP 5.5.

Fig. 6 - Specifying an FTP virtual host.

Scorecard for WinSCP 5.5

This concludes my quick look at a few of the FTP features that are available with WinSCP 5.5, and here are the scorecard results:

Client
Name
Directory
Browsing
Explicit
FTPS
Implicit
FTPS
Virtual
Hosts
True
HOSTs
Site
Manager
Extensibility
WinSCP 5.5.1 Rich Y Y Y N Y N/A
Note: I could not find anyway to extend the functionality of WinSCP 5.5; but as I said
earlier, it provides rich automation features for .NET, COM, and the command-line.

That wraps things up for today's blog. Your key take-aways should be: WinSCP 5.5 is good FTP client with a lot of options, and it has a very powerful automation story. As I mentioned earlier, if you have to do a lot of FTP automation, you should really take a look at this FTP client.


Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Adding Additional Content Types to my Classic ASP and URL Rewrite Samples for Dynamic SEO Functionality

28 February 2014 • by Bob • URL Rewrite, SEO, Classic ASP

In December of 2012 I wrote a blog titled "Using Classic ASP and URL Rewrite for Dynamic SEO Functionality", in which I described how to use Classic ASP and URL Rewrite to dynamically-generate Robots.txt and Sitemap.xml or Sitemap.txt files. I received a bunch of requests for additional information, so I wrote a follow-up blog this past November titled "Revisiting My Classic ASP and URL Rewrite for Dynamic SEO Functionality Examples" which illustrated how to limit the output for the Robots.asp and Sitemap.asp files to specific sections of your website.

That being said, I continue to receive requests for additional ways to stretch those samples, so I thought that I would write at least a couple of blogs on the subject. With that in mind, for today I wanted to show how you can add additional content types to the samples.

Overview

Here is a common question that I have been asked by several people:

"The example only works with *.html files; how do I include my other files?"

That's a great question, and additional content types are really easy to implement, and the majority of the code from my original blog will remain unchanged. Here's the file by file breakdown for the changes that need made:

FilenameChanges
Robots.asp None
Sitemap.asp See the sample later in this blog
Web.config None

If you are already using the files from my original blog, no changes need to be made to your Robots.asp file or the URL Rewrite rules in your Web.config file because the updates in this blog will only impact the output from the Sitemap.asp file.

Updating the Sitemap.asp File

My original sample contained a line of code which read "If StrComp(strExt,"html",vbTextCompare)=0 Then" and this line was used to restrict the sitemap output to static *.html files. For this new sample I need to make two changes:

  1. I am adding a string constant which contains a list of file extensions from several content types to use for the output.
  2. I replace the line of code which performs the comparison.

Note: I define the constant near the beginning of the file so it's easier for other people to find; I would normally define that constant elsewhere in the code.

<%
    Option Explicit
    On Error Resume Next
    
    Const strContentTypes = "htm|html|asp|aspx|txt"
    
    Response.Clear
    Response.Buffer = True
    Response.AddHeader "Connection", "Keep-Alive"
    Response.CacheControl = "public"
    
    Dim strFolderArray, lngFolderArray
    Dim strUrlRoot, strPhysicalRoot, strFormat
    Dim strUrlRelative, strExt

    Dim objFSO, objFolder, objFile

    strPhysicalRoot = Server.MapPath("/")
    Set objFSO = Server.CreateObject("Scripting.Filesystemobject")
    
    strUrlRoot = "http://" & Request.ServerVariables("HTTP_HOST")
    
    ' Check for XML or TXT format.
    If UCase(Trim(Request("format")))="XML" Then
        strFormat = "XML"
        Response.ContentType = "text/xml"
    Else
        strFormat = "TXT"
        Response.ContentType = "text/plain"
    End If

    ' Add the UTF-8 Byte Order Mark.
    Response.Write Chr(CByte("&hEF"))
    Response.Write Chr(CByte("&hBB"))
    Response.Write Chr(CByte("&hBF"))
    
    If strFormat = "XML" Then
        Response.Write "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
        Response.Write "<urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9"">" & vbCrLf
    End if
    
    ' Always output the root of the website.
    Call WriteUrl(strUrlRoot,Now,"weekly",strFormat)

    ' --------------------------------------------------
    ' This following section contains the logic to parse
    ' the directory tree and return URLs based on the
    ' files that it locates.
    ' -------------------------------------------------- 
    strFolderArray = GetFolderTree(strPhysicalRoot)

    For lngFolderArray = 1 to UBound(strFolderArray)
        strUrlRelative = Replace(Mid(strFolderArray(lngFolderArray),Len(strPhysicalRoot)+1),"\","/")
        Set objFolder = objFSO.GetFolder(Server.MapPath("." & strUrlRelative))
        For Each objFile in objFolder.Files
            strExt = objFSO.GetExtensionName(objFile.Name)
            If InStr(1,strContentTypes,strExt,vbTextCompare) Then
                If StrComp(Left(objFile.Name,6),"google",vbTextCompare)<>0 Then
                    Call WriteUrl(strUrlRoot & strUrlRelative & "/" & objFile.Name, objFile.DateLastModified, "weekly", strFormat)
                End If
            End If
        Next
    Next

    ' --------------------------------------------------
    ' End of file system loop.
    ' -------------------------------------------------- 
    If strFormat = "XML" Then
        Response.Write "</urlset>"
    End If
    
    Response.End

    ' ======================================================================
    '
    ' Outputs a sitemap URL to the client in XML or TXT format.
    ' 
    ' tmpStrFreq = always|hourly|daily|weekly|monthly|yearly|never 
    ' tmpStrFormat = TXT|XML
    '
    ' ======================================================================

    Sub WriteUrl(tmpStrUrl,tmpLastModified,tmpStrFreq,tmpStrFormat)
        On Error Resume Next
        Dim tmpDate : tmpDate = CDate(tmpLastModified)
        ' Check if the request is for XML or TXT and return the appropriate syntax.
        If tmpStrFormat = "XML" Then
            Response.Write " <url>" & vbCrLf
            Response.Write " <loc>" & Server.HtmlEncode(tmpStrUrl) & "</loc>" & vbCrLf
            Response.Write " <lastmod>" & Year(tmpLastModified) & "-" & Right("0" & Month(tmpLastModified),2) & "-" & Right("0" & Day(tmpLastModified),2) & "</lastmod>" & vbCrLf
            Response.Write " <changefreq>" & tmpStrFreq & "</changefreq>" & vbCrLf
            Response.Write " </url>" & vbCrLf
        Else
            Response.Write tmpStrUrl & vbCrLf
        End If
    End Sub

    ' ======================================================================
    '
    ' Returns a string array of folders under a root path
    '
    ' ======================================================================

    Function GetFolderTree(strBaseFolder)
        Dim tmpFolderCount,tmpBaseCount
        Dim tmpFolders()
        Dim tmpFSO,tmpFolder,tmpSubFolder
        ' Define the initial values for the folder counters.
        tmpFolderCount = 1
        tmpBaseCount = 0
        ' Dimension an array to hold the folder names.
        ReDim tmpFolders(1)
        ' Store the root folder in the array.
        tmpFolders(tmpFolderCount) = strBaseFolder
        ' Create file system object.
        Set tmpFSO = Server.CreateObject("Scripting.Filesystemobject")
        ' Loop while we still have folders to process.
        While tmpFolderCount <> tmpBaseCount
            ' Set up a folder object to a base folder.
            Set tmpFolder = tmpFSO.GetFolder(tmpFolders(tmpBaseCount+1))
              ' Loop through the collection of subfolders for the base folder.
            For Each tmpSubFolder In tmpFolder.SubFolders
                ' Increment the folder count.
                tmpFolderCount = tmpFolderCount + 1
                ' Increase the array size
                ReDim Preserve tmpFolders(tmpFolderCount)
                ' Store the folder name in the array.
                tmpFolders(tmpFolderCount) = tmpSubFolder.Path
            Next
            ' Increment the base folder counter.
            tmpBaseCount = tmpBaseCount + 1
        Wend
        GetFolderTree = tmpFolders
    End Function
%>

That's it. Pretty easy, eh?

I have also received several requests about creating a sitemap which contains URLs with query strings, but I'll cover that scenario in a later blog.


Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

The Book of Squirrels

27 February 2014 • by Bob • Humor

(Note: I found this on my computer, which I had posted to our refrigerator several years ago when my wife and I were going out of town for a few days and I wanted my son, Peter, to remember to put out food for the squirrels while my wife and I were away.)

BookOfSquirrels

Bob 21:15 - The lord of the house said to Peter Joshua, "Peter, do you love me more than these?" "Yes, Dad," he said, "you know that I love you." Bob said, "Feed my squirrels."

Bob 21:16 - Again the lord of the house said, "Peter, son of Robert, do you love me?" He answered, "Yes, Dad, you know that I love you." Bob said, "Take care of my squirrels."

Bob 21:17 - The third time he said to him, "Peter, son of Robert, do you love me?" Peter was hurt because his dad asked him the third time, "Do you love me?" He said, "Dad, you know many things; you know that I love you." Bob said, "Feed my squirrels."

(Note: If you don't get the reference, I'm not explaining it to you.)

Dear Unknown Pickup Truck Driver

26 February 2014 • by Bob • Bicycling

Dear Unknown Pickup Truck Driver,

I'd like to personally thank you for failing to see the bicyclist (me) in the clearly-marked bike lane and nearly hitting me. I flipped my bicycle to avoid running into you, so your brain-dead vehicular maneuver restored my belief that some people are too dumb to operate a car. Next time, try hanging up your @#$% cell phone and watch the road.

As luck would have it, I was riding to the hospital where my wife works as a nurse when this mishap occurred, and she used Tegaderm to bandage everything. (Although that was after the painful experience of scrubbing all of the dirt out of the wound.)

On the positive side, this was a great test of the brakes on my new bicycle. (FYI - The brakes work really well; maybe a little too well. But as they say, any landing you can walk away from...)

I should also mention that I'm very fortunate that minor bumps and gashes were the worst of my injuries; I was foolishly wearing neither my helmet nor my gloves when this happened. Both hands were a little bruised and my right arm had several nasty-looking scrapes, but nothing was broken. (And I have learned my lesson; I will always wear my helmet in the future.)

Hashtags: #itsokaythegroundbrokemyfall, #lookicanfly, #itsonlyafleshwound

Every Gene Pool has a Shallow End

17 February 2014 • by Bob • Humor

So I watched this video...

And that inspired me to create this image...

GOPRO

Enough said. Smile

Using ASX Files with Windows Media Center

16 February 2014 • by Bob • Windows Media Center

Like a lot of Windows geeks and fanboys, I use Windows Media Center on a Windows 7 system as my Digital Video Recorder (DVR) and media library. My system consists of a Dell GX270 computer with a ZOTAC NVIDIA GeForce GT610 video card, and it uses an InfiniTV 6 ETH tuner to receive cable signals. This setup has served us faithfully for years, and it is the center piece of our home entertainment system. If you're not familiar with Windows Media Center, that's because it's a rather hideously under-advertised feature of Windows. Just the same, here is an official Microsoft teaser for it:

But I've done a few extra things with my Windows Media Center that are a little beyond the norm, and one of the biggest items that I spent a considerable amount of time and effort digitizing my entire collection of DVD and Blu-ray discs as MP4 files, and I store them on a Thecus NAS that's on my home network which I use for media libraries on my Windows Media Center. This allows me to have all of my movies available at all times, and I can categorize them into folders which show up under the "Videos" link on the Windows Media Center menu.

That being said, there's a cool trick that I've been using to help customize some of my movies. Some of the movies that I have encoded have some material that I'd like to cut out, (like excessive opening credits and lengthy intermissions), but I don't want to edit and re-encode each MP4 file. Fortunately, Windows Media Center supports Advanced Stream Redirector (ASX) files, which allows me to customize what parts of a video are seen without having to edit the actual video.

Here's a perfect example: I recently purchased the 50th Anniversary Collector's Edition of Lawrence of Arabia on Blu-ray. The film is one of my favorites, and this reissue on Blu-ray is phenomenal. That being said, the movie begins with a little over four minutes of a blank screen while the musical overture plays. In addition, there is an additional eight minutes of a blank screen while the music for intermission is played. This is obviously less than desirable, so I created an ASX file which skips the opening overture and intermission.

By way of explanation, ASX files are XML files which define a playlist for media types, which can be any supported audio or video media. The individual entries can define various metadata about each media file, and thankfully can be used to specify which parts of a media file will be played.

With that in mind, here's what the ASX file that I created for Lawrence of Arabia looks like:

<ASX VERSION="3.0">
  <!-- Define the title for the movie. -->
  <TITLE>Lawrence Of Arabia</TITLE>
  <!-- Specify the movie's author. -->
  <AUTHOR>Columbia Pictures</AUTHOR>
  <!-- List the copyright for the movie. -->
  <COPYRIGHT>1962 Horizon Pictures (GB)</COPYRIGHT>
  <ENTRY>
    <!-- Define the video file for this entry. -->
    <REF HREF="Lawrence Of Arabia.mp4" />
    <!-- Define the start time for this entry. -->
    <STARTTIME VALUE="00:04:17.0"/>
    <!-- Define the duration for this entry. -->
    <DURATION VALUE="02:15:07.0"/>
  </ENTRY>
  <ENTRY>
    <!-- Define the video file for this entry. -->
    <REF HREF="Lawrence Of Arabia.mp4" />
    <!-- Define the start time for this entry. -->
    <STARTTIME VALUE="02:23:38.0"/>
  </ENTRY>
</ASX>

The XML comments explain what each of the lines in the file is configuring, and it should be straight-forward. But I would like to describe a few additional details:

  • Individual media entries are obviously defined in a collection of <ENTRY> elements, and in this example I have defined two entries:
    • The first entry defines a <STARTTIME> and <DURATION> which skip over the overture and play up to the intermission.
    • The second entry defines a <STARTTIME> which starts after the intermission and plays through the end of the movie.
  • The other metadata in the file - like the <AUTHOR> and <COPYRIGHT> - is just for me. That information is optional, but I like to include it.

There are several other pieces of metadata which can be configured, and a list of those are defined in the Windows Media Metafile Elements Reference and ASX Elements Reference.

Error 0x80070005 When Calling the FTP FlushLog Method

31 January 2014 • by Bob • FTP, Extensibility

I had an interesting question earlier today which I thought was worth sharing. One of my coworkers was trying to use the code sample from my Programmatically Flushing FTP Logs blog, and he was getting the following error:

Unhandled Exception: System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
   at Microsoft.Web.Administration.Interop.IAppHostMethodInstance.Execute()
   at Sample.Main() in c:\Projects\FtpTests\Program.cs:line 25

I knew that the code sample in my blog worked perfectly when I originally wrote it, so I figured that my coworker must be doing something wrong. (And every developer has said "It works on my computer..." at one time or other.) But I decided to give him the benefit of the doubt, so I copied the source code from my blog into a new Visual Studio project and I ran it.

Much to my surprise, I saw the same error that my coworker was seeing if I didn't step the code through with a debugger.

When I stepped through the code in a debugger, I saw the following error message:

At this point I was thinking, "What the heck? I know this code was working before..." I started to wonder if we had released a breaking change to the FTP service sometime during the past two years, but then it suddenly dawned on me: I hadn't started the FTP service on my computer.

[Duh.]

That was the source of the problem: I usually have the FTP service configured for manual startup on my development computers, but the FTP methods to start and stop FTP sites and flush the FTP logs do not work when the FTP service is not running. Once both of us started the FTP service on each of our systems the problem went away.

I hope this helps. ;-]


Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Rapid PHP Deployment for IIS using a Batch File

30 January 2014 • by Bob • IIS, Scripting, IIS Express, PHP

Whenever I am delivering a presentation where I need to use PHP, I typically use a batch file that I wrote in order to rapidly deploy PHP on the system that I am using for my demos. The batch file usually takes less than a second to run, which always seems to amaze people in the audience. As a result, I usually have several people ask me for my batch file after each presentation, so I thought that it would make a good subject for today's blog.

I should mention that I have used this batch file in order to demonstrate PHP with IIS in a variety of scenarios, and one of my favorite demos is when I would borrow someone's laptop and plug in a flash drive where I had IIS Express pre-installed, and then I would run the batch file in this blog to deploy PHP. Next I would launch IIS Express, open a web browser on their system, and then browse to http://localhost/ in order to show that IIS Express was working correctly. Lastly I would write a simple PHP "Hello World" page to show that PHP was up-and-running on their system in a matter of seconds.

That being said, I have to point out that there is a very important prerequisite that you must have in order to follow the steps in the blog: you need to start with a known-good installation of PHP from one of your systems, and I'll explain what I mean by that.

My batch file expects to find a folder containing ready-to-run files for PHP in order to deploy PHP on a new system. I originally obtained my PHP files by using the Web Platform Installer (WebPI) to install PHP, and then I copied the files to my flash drive or some other repository. (Note that WebPI usually installs PHP in the "%ProgramFiles(x86)%\PHP" folder.) If you don't want to use WebPI, you can also download PHP from http://windows.php.net/, but you're on your own for configuration.

Once I have the files from a known-good installation of PHP, I create the following folder structure in the location where I will be storing the files that I use to deploy PHP on other systems:

  • <root folder>
    • SETUP_PHP.cmd (the batch file from this blog)
    • PHP (the folder containing the PHP files)
      • PHP.INI
      • PHP-CGI.EXE
      • etc. (all of the remaining PHP files and folders)

One thing to note is that the PHP.INI file you use may contain paths which refer to specific directories on the system from which you are copying your PHP files, so you need to make sure that those paths will exist on the system where you deploy PHP.

Here is an example: when I used WebPI to install PHP 5.5 on a system with IIS, it installed PHP into my "%ProgramFiles(x86)%\PHP\v5.5" folder. During the installation process, WebPI updated the PHP file to reflect any paths that need to be defined. At the time that I put together my notes for this blog, those updates mainly applied to the path where PHP expects to find it's extensions:

extension_dir="C:\Program Files (x86)\PHP\v5.5\ext\"

What this means is - if you want to deploy PHP to some other path on subsequent systems, you will need to update at least that line in the PHP.INI file that you are using to deploy PHP. In my particular case, I prefer to deploy PHP to the "%SystemDrive%\PHP" path, but it can be anywhere as long as you update everything accordingly.

The following batch file will deploy the PHP files in the "%SystemDrive%\PHP" folder on your system, and then it will update IIS with the necessary settings for this PHP deployment to work:

@echo off

REM Change to the installation folder
pushd "%~dp0"

REM Cheap test to see if IIS is installed
if exist "%SystemRoot%\System32\inetsrv" (
  REM Check for the PHP installation files in a subfolder
  if exist "%~dp0PHP" (
    REM Check for an existing installation of PHP
    if not exist "%SystemDrive%\PHP" (
      REM Create the folder for PHP
      md "%SystemDrive%\PHP"
      REM Deploy the PHP files
      xcopy /erhky "%~dp0PHP\*" "%SystemDrive%\PHP"
    )
    pushd "%SystemRoot%\System32\inetsrv"
    REM Configure the IIS settings for PHP
    appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='%SystemDrive%\PHP\php-cgi.exe',monitorChangesTo='php.ini',activityTimeout='600',requestTimeout='600',instanceMaxRequests='10000']" /commit:apphost
    appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='%SystemDrive%\PHP\php-cgi.exe',monitorChangesTo='php.ini',activityTimeout='600',requestTimeout='600',instanceMaxRequests='10000'].environmentVariables.[name='PHP_FCGI_MAX_REQUESTS',value='10000']" /commit:apphost
    appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='%SystemDrive%\PHP\php-cgi.exe',monitorChangesTo='php.ini',activityTimeout='600',requestTimeout='600',instanceMaxRequests='10000'].environmentVariables.[name='PHPRC',value='%SystemDrive%\PHP']" /commit:apphost
    appcmd.exe set config -section:system.webServer/handlers /+"[name='PHP_via_FastCGI',path='*.php',verb='GET,HEAD,POST',modules='FastCgiModule',scriptProcessor='%SystemDrive%\PHP\php-cgi.exe',resourceType='Either']" /commit:apphost
    popd
  )
)
popd

Once you have all of that in place, it usually takes less than a second to deploy PHP, which is why so many people seem interested during my presentations.

Note that you can deploy PHP for IIS Express just as easily by updating the "%SystemRoot%\System32\inetsrv" paths in the batch file to "%ProgramFiles%\IIS Express" or "%ProgramFiles(x86)%\IIS Express" paths. You can also use this batch file as part of a deployment process for PHP within a web farm; in which case, you will need to pay attention to the paths inside your PHP.INI file which I mentioned earlier.


Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

How Hippies Destroyed America

30 January 2014 • by Bob • Politics, Rants

Someone recently posted the following image on Facebook, and even though I know they were simply trying to be amusing, I found it highly offensive... (for reasons which I will explain in a moment).

hippies-are-vermin

Unfortunately, posting an image such as this reveals how little someone actually knows about how much damage "Flower Power" and the so-called "Love Movement" did to America. While hippies may have been right about some things, (like environmental responsibility and ecological activism), they were dead wrong about most others. Here is a brief summary of a few of the lasting effects that the single generation of 1960s-era youth had on society: an out-of-control drug culture, the unchecked rise in numerous sexually-transmitted diseases, hundreds of thousands of PTSD cases of veterans traumatized by counter-culture attacks, and the embarrassment of our nation in the eyes of the rest of the world.

When you follow the emergence of the hippie movement, it is one that outwardly preached living in harmony with all of society, and yet inwardly its actualization was one of extreme selfishness and unbridled, destructive power. Timothy Leary's invitation to "Turn On, Tune In, Drop Out" encouraged a generation of youth to abstain from any semblance of civil and moral responsibility in favor of seeking personal, self-centered desires. In the span of a few short years, the hippies managed to negate nearly all of the hard-won victories of our country's "Greatest Generation," (those who banded together to survive the Great Depression and win the Second World War). Our country descended from an industrious world leader populated by hard-working, family-oriented citizens to a vicious brood of misguided, distrustful, lazy, addicted, self-worshippers.

Like much of the hippie movement, the so-called "Summer of Love" is something of an oxymoron, because it achieved the opposite of its intended goals. When thousands of lost youths descended on the Haight-Ashbury district of San Francisco, they did so with bold proclamations of free love, uninhibited creativity, and peace-for-all. Yet the size of this group collapsed the infrastructure of the local area, which was unprepared to deal with the sudden arrival of thousands of drugged-out, socially irresponsible vagrants. This should have been one of society's first warnings about the pure selfishness of the hippy mindset, but this event was largely ignored by anyone except other teenagers and twenty-somethings who were tired of listening to their parents telling them to grow up, get a job, and contribute something to society other than folk songs and clouds of pot smoke.

One of the rallying cries for the hippie movement was a general objection to the Vietnam War, and while I agree that anyone in their right mind should oppose war as best as possible, the hippies reacted in the worst possible way. Instead of gathering peacefully across the country, hippies engaged in numerous cases of what would now be referred to as "Domestic Terrorism." In their naïveté, thousands of youths openly proclaimed their support for Marxism/Leninism/Communism to overthrow the government of the United States, even though none of these impressionable youths had ever lived under such oppressive regimes, and many of these same degenerates would not have lasted a year if they had emigrated to the USSR.

Please do not misunderstand me - I fully support the right to peaceful assembly and vociferously objecting to war, both then and now, but there are proper ways to do so - and conversely, there are improper ways to be avoided. For example: hippies used to call my mom in the late 1960s while my dad was stationed in Vietnam, and they would pretend to be the Department of the Air Force calling to inform my mom that my dad had been killed in combat. This happened many, many times - and she would hug my brothers and me as she wept inconsolably for hours; my mom's life was probably shortened by several years due to insufferable grief caused by the heinously evil and unnecessary actions of these particular vermin who called themselves hippies.

There are two things that can be learned from the hippies' response to the Vietnam War:

  1. War is a terrible thing which motivates some people to do terrible things.
  2. The way some people choose to protest war is far worse.

Tragically, my experiences were not isolated incidents; the history of the Vietnam War on the home front is rife with examples of the complete failure on the part of the hippy movement to make their protests known while still treating veterans returning from battle like fellow human beings. (Many of these veterans were draftees instead of volunteers, and therefore they had no say in their years of military service.)

Some of the most-damaging aspects of hippie culture were the concepts of "Open Marriages," "Free Love," etc. In their efforts to rid themselves of any vestige of what they believed were their parents' outdated sensibilities, hippies managed to convince themselves that committed, monogamous relationships were a thing of the past, and they substituted "Do What Feels Good For You" casual relationships in their place. There is an age-old axiom which states, "Why buy the cow when the milk is free," and in keeping with that notion, the men of the hippy generation managed to convince the women of that era to abandon their morality in what was probably the most-condescending deception of women in the history of the United States. To quote Steve Martin, "Free Love ... was the single greatest concept a young man has ever heard. This was a time when intercourse, or some version of it, was a way of saying hello. About three years later, women got wise and my frustration returned to normal levels (Martin 2007, 100)." Despite the ill-guided assertions that the hippy movement gave birth to the Women's Liberation movement of the following decade, male hippies treated their female counterparts little better than objects for their own, self-desires. As a direct result, a conflagration of sexually-transmitted diseases spread across the country like a raging inferno, divorce rates skyrocketed, and millions of children were forced to grow up in single-parent homes due to the hippy-based philosophy that marriages need not be permanent.

Ultimately the hippy movement was a complete failure of society on both sides of equation: the hippies failed to behave in any fashion which reflects the better ideals of humanity, and the United States' government failed to effectively respond to the subculture which infested much of the Baby Boomer generation. Our nation still bears numerous scars from societal wounds inflicted by the selfish and amoral youth of the 1960s, and history will eventually reveal that their actions irrevocably damaged the fabric of our culture and hastened the demise of our once-great country.

On a personal note - forty-five years have passed since the time when my family was individually targeted and tormented by faceless cowards who publicly preached love for their fellow man while privately living for their own selfish gains. I have neither forgiven nor forgotten the traumatic pain that these so-called "Peace Loving Hippies" caused my family and our nation to suffer.


Martin, Steve. Born Standing Up: A Comic's Life. New York, NY: Simon & Shuster, Inc., 2007.

Blog Navigation

You are on page 49 of 73 pages.

Disclaimer

All content within this blog represents my personal views and opinions only. This content is not intended to represent the views, positions, or strategies of my employer or any other organization with which I may be associated. All content and code samples are provided "as is" without warranty of any kind.