For My Wife: Growing Old Together

Valentine's Day is just around the corner, which is always an occasion for me to become a little introspective. With that in mind, I remember the days of our courtship when we would promise to love each other forever and to grow old together; yet now as I look back on our lives, I realize that we had no idea what we were saying. We were young and in love and completely clueless about what being in love really meant.

30 - Rings

Mark Twain once wrote that "No man or woman really knows what perfect love is until they have been married a quarter of a century," and now that we have passed that milestone I can look back and begin to catch a glimpse of this elusive concept called "true love."

Love has meant staying together through times of destitute poverty when we didn't know from where our next meal would come. Love has meant enduring months of separation when I was serving abroad in our country's armed forces. Love has meant countless sleepless nights raising children and meeting their every need. Love has meant staying by each other's bedside to nurse one another back to health. Love has meant walking side-by-side through that timeless season of joy mixed with pain that all parents must suffer when watching their children grow up and leave home.

Over the years I have learned that true love is not the offspring of well-meant promises made hastily in your youth; true love is borne of a thousand little things over thousands of days and nights as you grow older together, until you find that so much time has passed that you cannot remember a time when you were ever apart.

Will Durant wrote that "The love we have in our youth is superficial compared to the love that an old man has for his old wife," and I have found my greatest joy in growing old with you.

The Twelve Commandments of Flaming (Revisited)

Twenty years ago or so, even before the time of Al Gore, there was this thing called "The Internet." There were two kinds of people that used it: the military types and college types. As I was transitioning out of the military following the first Gulf War, I fell into both categories. There was no "World Wide Web" then - which some of today's younger generation cannot understand. ("How could you find anything before companies had websites?")

Back then you pretty much had to be a geek to be on the Internet; it had not yet been reduced to the fewer-than-140-character drivel that is so prevalent on today's Internet. That being said, I spent my time on the Internet using four technologies:

  • FTP - for retrieving files
  • Gopher - for research
  • Email - for keeping in touch with some of my ex-military friends
  • Newsgroups - for arguing

Gopher is long-since deceased, FTP is still in use but its power is waning, and if you're younger than the age of 20 then you probably spend more time with Facebook, Twitter, and cell phone texting than sending emails. (But wait until you move out, get a job, and start having to make a living and pay for your own existence - which includes things like rent and food, not just Starbucks and cell phone service.)

In any event, I used to spend a lot of time hanging out in various newsgroups arguing all sorts of issues and topics. Even when I agreed with someone I argued with them. That's really what newsgroups were for - so geeks from all over the planet could argue with each other. Over the years newsgroups have mostly been replaced by web-based forums, although the comments sections on blogs seem to have grown into the avenue-of-choice where the real arguing takes place.

Back in the pre-WWW days, someone put together the "The Twelve Commandments of Flaming," and I wish that I knew the original author's name. (I have seen it attributed to many different people over the years.) What is most amusing about this list is how true it was both then and now. For examples of such behavior, read this list, then browse to your favorite blog and read the comments section.


The Twelve Commandments of Flaming

  1. Make things up about your opponent: It's important to make your lies sound true. Preface your argument with the word "clearly."

    "Clearly, Fred Flooney is a liar, and a dirtball to boot."

  2. Be an armchair psychologist: You're a smart person. You've heard of Freud. You took a psychology course in college. Clearly, you're qualified to psychoanalyze your opponent.

    "Polly Purebread, by using the word 'zucchini' in her posting, shows she has a bad case of penis envy."

  3. Cross-post your flames: Everyone on the net is just waiting for the next literary masterpiece to leave your terminal! From the Apple II RoundTable to X-10 Powerhouse RoundTable, they're all holding their breath until your next flame. Therefore, post everywhere.
  4. Conspiracies abound: If everyone's against you, the reason can't possibly be that you're a jerk. There's obviously a conspiracy against you, and you will be doing the entire net a favor by exposing it.
  5. Lawsuit threats: This is the reverse of Rule #4 (sort of like the Yin & Yang of Flaming). Threatening a lawsuit is always considered to be in good form.

    "By saying that I've posted to the wrong group, Bertha has libeled me, slandered me, and sodomized me. See you in court, Bertha."

  6. Force them to document their claims: Even if Harry Hoinkus states outright that he likes tomato sauce on his pasta, you should demand documentation. If Newsweek hasn't written an article on Harry's pasta preferences, then Harry is obviously lying.
  7. Use foreign phrases: French is good, but Latin is the lingua franca of flaming. You should use the words "ad hominem" at least three times per article. Other favorite Latin phrases are "ad nauseum," "veni, vidi, vici," and "fetuccini alfredo."
  8. Tell 'em how smart you are: Why use intelligent arguments to convince them you're smart when all you have to do is tell them? State that you're a member of Mensa, or Mega, or Dorks of America. Tell them the scores you received on every exam since high school.

    "I got an 800 on my SATs, LSATs, GREs, MCATs, and I can also spell the word 'premeiotic' ."

  9. Accuse your opponent of censorship. It is your right as an American citizen to post whatever the hell you want to the net (as guaranteed by the 37th Amendment, I think). Anyone who tries to limit your cross-posting or move a flame war to email is either a communist, a fascist, or both.
  10. Doubt their existence: You've never actually seen your opponent, have you? And since you're the center of the universe, you should have seen them by now, shouldn't you? Therefore, THEY DON'T EXIST! This is the beauty of flamers' logic.
  11. Lie, cheat, steal, leave the toilet seat up.
  12. When in doubt, insult: If you forget the other 11 rules, remember this one. At some point during your wonderful career as a Flamer you will undoubtedly end up in a flame war with someone who is better than you. This person will expose your lies, tear apart your arguments, make you look generally like a bozo. At this point, there's only one thing to do: INSULT THE DIRTBAG!!!

    "Oh yeah? Well, your mother does strange things with vegetables."

An Example for the Rookie Flamer

> Dear Joe,
I object to your use of the word "dear." It shows you are a condescending, sexist pig. Also, the submissive tone you use shows that you like to be tied down and flagellated with licorice whips.
> While I found your article "The Effect of Belly-Button Lint
> on Western Thought" to be extremely thought-provoking,
"Thought-provoking?" I had no idea you could think, you rotting piece of swamp slime.
> it really shouldn't have been posted in rec.scuba.
What? Are you questioning my judgment? I'll have you know that I'm a member of the super-high-IQ society Menstruate. I got an 800 on my PMS exam. Your attempts constitute nothing less than censorship. There is a conspiracy against me. You, Riff Raff, and Simon Sinister have been constantly harassing me by email. This was an ad hominem attack! I have therefore cross-posted this to alt.flame, rec.nude, comp.graphics, and rec.arts.wobegon.
> Perhaps you should have posted it in misc.misc.
It is my right, as granted in the Bill of Rights, the Magna Carta, the Bible and the Koran, to post where ever I want to. Or don't you believe in those documents, you damn fascist? Perhaps if you didn't spend so much time sacrificing virgins and infants to Satan, you would have realized this.
> Your article would be much more appropriate there.
Can you document this? I will only accept documents notarized by my attorney, and signed by you in blood. Besides, you don't really exist anyway, you AI project, you.

And in closing...

Flames should be witty, insulting, interesting, funny, caustic, or sarcastic, but NEVER, EVER, should they be boring.

And the walls came tumbling down...

Today is November 9th - which marks the 20-year anniversary of the re-opening of the borders in Germany, which ultimately resulted in the fall of Soviet Communism. While most of us remember where we were on 9/11/2001, I also remember where I was on 11/9/1989 - I was on the East German border, helping to keep the Russian 8th Guards Army at bay...

For all the hype about the Cold War, Nuclear Proliferation, and Mutually-Assured Destruction that we had way back when, at least we knew who they bad guys were and where they were hiding. I have often said that I loved what I did back then, and that’s still a true statement. That being said, I must admit that I have enough memories to last a dozen lifetimes of sleepless nights in sub-zero temperatures chasing signals through the RF spectrum or standing guard duty in some dark corner of the world where even the evil empire had the good sense to avoid. But the simple fact is - business was good during the Cold War, right up to the time when peace broke out and ruined my life and I had to get a real job. ;-)

In any case, I tip my hat to my fellow members of the Fulda Fighting 511th and the Bat Cave Dwellers of Fort Huachuca. Even though it’s grammatically incorrect to express it this way, it seems appropriate for me to say: "We did good."

School in 1959 versus 2009

I was sent this list of how several things have changed in our educational system and lives over the past 50 years, and it's a sad but true observation of how "Trying to Make Things Better™" ultimately makes things worse...

SCENARIO 1: Jeffrey will not be still in class, he disrupts other students.

  • 1959 - Jeffrey sent to the Principal's office and given a good paddling by the Principal. He then returns to class, sits still and does not disrupt class again.
  • 2009 - Jeffrey is given huge doses of Ritalin. He becomes a zombie. He is then tested for A.D.D. The school gets extra money from the state because Jeffrey has a disability.

SCENARIO 2: Johnny and Mark get into a fist fight after school.

  • 1959 - Crowd gathers. Mark wins. Johnny and Mark shake hands and end up buddies.
  • 2009 - Police called and SWAT team arrives -- they arrest both Johnny and Mark. They are both charged with assault and both expelled, even though Johnny started it.

SCENARIO 3: Mark gets a headache and takes some aspirin to school.

  • 1959 - Mark shares his aspirin with the Principal out on the smoking dock.
  • 2009 - The police are called and Mark is expelled from school for drug violations. His car is then searched for drugs and weapons.

SCENARIO 4: Jack goes quail hunting before school and then pulls into the school parking lot with his shotgun in his truck's gun rack.

  • 1959 - Vice Principal comes over, looks at Jack's shotgun, goes to his car and gets his shotgun to show Jack.
  • 2009 - School goes into lock down, FBI called, Jack hauled off to jail and never sees his truck or gun again. Counselors called in for traumatized students and teachers.

SCENARIO 5: Billy breaks a window at his school and his Dad gives him a whipping with his belt.

  • 1959 - Billy is more careful next time, grows up normal, goes to college and becomes a successful businessman.
  • 2009 - Billy's dad is arrested for child abuse. Billy is removed to foster care and joins a gang. The state psychologist is told by Billy's sister that she remembers being abused herself and their dad goes to prison. Billy's mom has an affair with the psychologist.

SCENARIO 6: Pedro fails high school English.

  • 1959 - Pedro goes to summer school, passes English and goes to college.
  • 2009 - Pedro's cause is taken up by state. Newspaper articles appear nationally explaining that teaching English as a requirement for graduation is racist. ACLU files class action lawsuit against the state school system and Pedro's English teacher. English is then banned from core curriculum. Pedro is given his diploma anyway but ends up mowing lawns for a living because he cannot speak English.

SCENARIO 7: Johnny takes apart leftover firecrackers from the Fourth of July, puts them in a model airplane paint bottle and blows up a red ant bed.

  • 1959 - Ants die.
  • 2009 - ATF, Homeland Security and the FBI are all called. Johnny is charged with domestic terrorism. The FBI investigates his parents -- and all siblings are removed from their home and all computers are confiscated. Johnny's dad is placed on a terror watch list and is never allowed to fly again.

SCENARIO 8: Johnny falls while running during recess and scrapes his knee. He is found crying by his teacher, Mary. Mary hugs him to comfort him.

  • 1959 - In a short time, Johnny feels better and goes on playing.
  • 2009 - Mary is accused of being a sexual predator and loses her job. She faces 3 years in State Prison. Johnny undergoes 5 years of therapy.

Whatever happened to common decency?

I realize that we live in a stressful time - so many people are concerned about major events such as the war, the spiraling economy and housing market, the erosion of employer loyalty in an ever-changing job environment, etc. But every once in a while you are treated so badly by your fellow person that it's enough to make you sit up and wonder aloud at what's going on. On two separate occasions within the last week I have been witness to some of the rudest actions that I have seen in recent months. Either event was remarkable by itself, but compounded so much more so by the severity of the discourteous behavior that I witnessed in such a short time.

This past week my wife and I took our children to the movies. As we pulled into the theater's parking lot, I chose a space next to a large SUV that had managed to squeeze into a space that was clearly designed and labeled for a compact car. This space was at the end of a row of cars, and I pulled in so close to the sidewalk on the opposite side of the gas-guzzling gargantuan that my tires were grazing the curb as I parked; because of this action I was able to make sure that there was still plenty of room between both vehicles for people to enter or exit safely.

After the movie we walked to the parking lot where I noticed a note attached to my windshield. I read the note aloud which stated, "I have your license information and pictures. I'll call the police if there's any damage." My initial thought was, "Oh no, someone hit my car." For some reason it just didn't register with me at first what was actually being said, so my son and I walked around our car to inspect for damage. We didn't see anything wrong, so I reread the note aloud a couple more times and then it hit me - the author of the note was threatening me not to damage their car.

Never mind the fact that this automotive monster was located in a space too small for its size, the driver of this mammoth on wheels was concerned that I might somehow injure their precious beast. I looked at my son and joked, "If I wasn't such a nice person, I'd kick the door on the other side of their car." We both laughed at the thought, and since I had parked so close to the curb on the opposite side of the behemoth, my son was able to make use of the ample room between the vehicles to open our car door and get in for the short drive home. (Note: Because of my disbelief that the parking lot event actually occurred, I kept the threatening note and it now adorns the bulletin board in my office.)

The fact of the matter is, however, that even though the driver of that SUV didn't really need to write that note, it was up to me to choose how best to react. In some aspects, this could have provided ample opportunity for an angry "Pay It Forward" approach to the rest of my evening or the days ahead. I could have left an equally threatening note, or I could have damaged their vehicle out of spite. Instead, I chose to make a short joke to voice my frustration and drove home peaceably.

But sometimes it's hard to remain so detached.

Recently I needed to catch a flight home on Southwest Airlines, which prides itself for on-time flights and unassigned seating. Since I fly on Southwest Airlines fairly often, I know that I should get to the airport a little earlier to get in line for general boarding. When I arrived at the gate, I found a half-dozen people sitting in line already, and I dropped my carryon luggage at the end of the queue and sat down in the line. Over the next hour, several other travelers showed up and orderly took their places behind each other. Occasionally a person or two would mistake where the line was and walk to the front of the line, and would happily relocate to the end of the line when notified of their mistake by the people ahead of me.

A few minutes before general boarding was to begin, a few of the passengers at the front of the line began to stand up. Right as I got to my feet, a couple walked up to the gate and proceeded to get in line in front of the row of waiting passengers. I pointed to the people that were sitting in line and said, "These people are in line already; the end of the line is back there" and motioned to the back of the queue. The would-be line jumper remarked that none of the passengers that I had just pointed to were actually standing in line, they were sitting, so therefore they weren't really in line. I informed him that all of these people had been there at least an hour already, and that he needed to relocate to the rear of the column. He reluctantly obliged after a brief and curt exchange of words with all of the surrounding passengers, albeit complaining the entire way that he travels 120 flights per year on Southwest Airlines. After taking his place at the end of the line, he loudly exclaimed in my direction, "Yeah, don't even look this way, ---hole!" I turned around and stared at him in disbelief, while the other people in line began to laugh at him for creating such a ruckus over having to board the plane perhaps one or two minutes later. Was any of this really called for?

Once again, however, this left me with the choice on how I was going to react. I could have followed his example and launched into a tirade of expletives, but why stoop to that level? (I must admit, however, that I did take advantage of a situation that happened moments later. A person came running up just as I was about to board the plane, and he was holding a large, boxed painting. He said that he was supposed to pre-board but had trouble getting through security. I could see that he had a pre-boarding stamp on his boarding pass, and I just couldn't resist the situation - I looked at him, then I looked back at the would-be line jumper, then I looked back at the gentleman with the boarding pass and said, "Please - be my guest," and gestured for him to board ahead of me.)

Okay, I must admit - perhaps the last part of that story was a bit over the top on my part, but I just couldn't help myself. But once again, the line jumper was way out of line. He was wrong, but insisted that everyone accept his behavior. When the surrounding crowd forced him to take his rightful place in line, he did so with a never-ending stream of complaints, insults, and expletives. That just isn't right.

So I have to ask the question again; whatever happened to common decency?

To the Moon... or Bust...

I have always been fascinated by all things space-related; the moon, the planets, the stars, etc. Perhaps it was watching all of the live TV broadcasts during the race to the moon in the 1960's, or perhaps it was spending part of my childhood living in Florida where astronauts would drop by the schools to talk about what they did for a living. My dad probably had a lot to do with this space-age attraction; when we were living in Tampa, he got us up early one morning and drove across Florida so we could be there to watch Apollo 16 launch for what was to become one of the last manned missions to the moon, and he bought the telescope that I continue to use to this day.

In any case, I was fascinated with space throughout my childhood. Over the years I followed the stories about the development of the space shuttle program, I watched several of the shuttle launches on television, and I even managed to make it back to Florida to attend one of the shuttle launches.

I tell you all of these things just to set the stage - when I heard that NASA was going to smash a rocket into the surface of the moon at 7:30am EST on October 9th, 2009, my first thought was shared by many people around the globe: "What gives NASA the right to bomb the moon?"

This was followed quickly by my second thought, which was: "Cool - can I watch?"

In fact, NASA had said that the explosion would be large enough to be seen by amateur enthusiasts, which is why I stayed up all night on October 8th to wait for it. I set up my telescope in the backyard, and I had a computer nearby that was streaming the live video feed from NASA. So I waited, and I watched, and I waited, and I watched, and then... nothing happened. There came a time when all of the folks at NASA's Ames Research Center in California started cheering, and it became obvious that whatever was going to happen had just done so, but not only was it impossible to see anything through my telescope, it was impossible to see anything on NASA's cameras that were smashing into the moon to record the event.

When you study the history of NASA's exploratory missions to places like Mars, you see a long pattern of failures due to one problem or other, and a lot of very expensive equipment has met a tragic end by plowing into the surface of Mars. So now that NASA has a chance to make up for their bad track record by intentionally crashing something into alien soil, you'd like to think that they've had enough practice to get that one thing right.

But unfortunately, they didn't.

Welcome to my blog...

How many times have you seen words on a web page that say something to the effect of "Welcome to my blog..." on some anonymous person's web site? Does that make you really feel welcome there? I don't think so, because - let's face it - their blog is about them, and the Internet is supposed to be about you, isn't it? If nothing else, these days the web is pretty much a breeding ground for narcissism.

I mean, think about it - all of the big sites on the Internet are focused on you: there's MYspace, YOUtube, and MYlife, etc. The other big sites, Facebook and Twitter aren't named after you, but be serious - who else are they about? Everyone wants to brag about their number of friends, or their followers, or their site hits, or whatever. Everyone wants to post about themselves, or blog about themselves, or tweet about themselves... but no one really wants to read what you're saying because they're too busy posting something about themselves. And even when you write a blog, everyone else wants to post their thoughts about what you just posted - as if you care, because you just wanted to post something about you.

The wonderful folks at www.despair.com put it this way:

So - with all that in mind, why in the world would I bother to start another blog that will do little more than inundate the Internet with more senseless drivel?

Once again, the folks at www.despair.com created a great poster that says it all:

And on that note, that's enough for today. Smile

eWeek Reviews for IIS 7.5 and FTP 7.5

One of my coworkers, Vijay Sen, just forwarded the following eWeek review of IIS 7.5 to me:

The review was written by Jim Rapoza, and he said some great things about IIS 7.5, which ships with both Windows Server 2008 R2 and Windows 7 client. But what really made my day was the following things that he said about FTP 7.5:

Another welcome change in IIS 7.5 is the elevation of FTP as a full-fledged part of the server. In previous versions, setup and management of an FTP server in IIS were done pretty much separately from Web server management. In IIS 7.5, FTP administration is fully integrated into the IIS Management Console.

I found this to be a very good implementation of FTP, making it possible to quickly set up secure FTP servers and tie them to my Websites. Especially nice was the ability to easily use virtual host names for the FTP sites. All in all, the FTP implementation in IIS 7.5 is one of the best I've seen, even when compared with dedicated FTP server products.

It's great to see all of our hard work being recognized!

Open-mouthed smile

My thanks once again to everyone on the FTP and IIS feature teams that helped make this version of the FTP service: Jaroslav, Emily, Daniel, Umer, Suditi, Ciprian, Jeong, Dave, Andrew, Carlos, Brian, Wade, Ulad, Nazim, Reagan, Claudia, Rick, Tim, Tobin, Kern, Jenny, Nitasha, Venkat, Vijay. (I hope that I didn't leave anyone out!)


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

FTP 7.5 Extensibility and Visual Studio Express Editions

In earlier blog posts I have mentioned that I written the several walkthroughs to help developers get started writing providers for the FTP 7.5 service, all of which available on Microsoft's learn.iis.net Web site under the "Developing for FTP 7.5" section. In each of these walkthroughs I wrote the steps as if you were using Visual Studio 2008.

Following up on that, I received a great question yesterday from a customer, Paul Dowdle, who wondered if it was possible to write an extensibility provider for the FTP 7.5 service using one of the Visual Studio Express Editions. By way of coincidence, I used to install Visual C# Express Edition on my laptop when I was traveling around the world to speak at events like TechEd. I usually did this because the Express Edition took up less hard drive space than a full installation of Visual Studio, and I was only writing code in C# on my laptop.

To answer Paul's question, the short answer is - yes, you can use Visual Studio Express Editions to develop custom providers for the FTP 7.5 service, with perhaps a few small changes from my walkthroughs.

For example, if you look at my "How to Use Managed Code (C#) to Create a Simple FTP Authentication Provider" walkthrough, in the section that is titled "Step 1: Set up the Project Environment", there is an optional step 6 for adding a custom build event to register the DLL automatically in the Global Assembly Cache (GAC) on your development computer.

When I installed Microsoft Visual C# 2008 Express Edition on a new computer, I didn't have the "%VS90COMNTOOLS%" environment variable or the "vsvars32.bat" file, so I had to update the custom build event to the following:

net stop ftpsvc
"%ProgramFiles%\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe" /if "$(TargetPath)"
net start ftpsvc

Once I made that change, the rest of the walkthrough worked as written.

So, to reiterate my earlier statement - you can use Visual Studio Express Editions to develop custom providers for the FTP 7.5 service. My thanks to Paul for the great question!


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

Merging FTP Extensibility Walkthroughs - Part 2

I had not intended to do a series on this subject when I wrote my original Merging FTP Extensibility Walkthroughs blog post, but I came up with a scenario that I felt was worth sharing. I recently posted the following walkthrough on the learn.iis.net web site:

How to Use Managed Code (C#) to Create an FTP Authentication Provider with Dynamic IP Restrictions

We have had many customer requests for a dynamic IP restrictions provider for the FTP server, and I wanted to get that out to customers as soon as I could. That being said, like several of my extensibility walkthroughs in the past, I wrote and tested the provider in that walkthrough on one of the servers that I manage. To show how effective it was, within the first couple of hours the provider had caught and blocked its first script kiddie who was attempting a brute force attack on my FTP server. Over the next few days the provider caught its next hacker, and over the past few weeks it has continued to do so.

That being said, I thought that it might be nice to know when an IP address was blocked, and I had already written the following walkthrough:

How to Use Managed Code (C#) to Create an FTP Provider that Sends an Email when Files are Uploaded

With that in mind, merging the two walkthroughs seemed like a simple thing to do.

Before continuing I need to reiterate the notice that I added to the dynamic IP restrictions walkthrough:

IMPORTANT NOTE: The latest version of the FTP 7.5 service must be installed in order to use the provider in this walkthrough. A version FTP 7.5 was released on August 3, 2009 that addressed an issue where the local and remote IP addresses in the IFtpLogProvider.Log() method were incorrect. Because of this, using an earlier version of the FTP service will prevent this provider from working.

With that warning out of the way, here are the steps that you need to follow in order to merge the two walkthroughs:

Step 1 - Create the project

Create a new C# project following all of the steps in the How to Use Managed Code (C#) to Create an FTP Authentication Provider with Dynamic IP Restrictions walkthrough.

Step 2 - Merge global variables

In this step you need to merge the global variables from the two walkthroughs. In my provider this looked like the following:

// Define the default values - these are only
// used if the configuration settings are not set.
const int defaultLogonAttempts = 5;
const int defaultFloodSeconds = 30;
const int defaultSmtpPort = 25;

// Define a connection string with no default.
private static string _connectionString;

// Initialize the private variables with the default values.
private static int _logonAttempts = defaultLogonAttempts;
private static int _floodSeconds = defaultFloodSeconds;

// Flag the application as uninitialized.
private static bool _initialized = false;

// Define a list that will contain the list of flagged sessions.
private static List<string> _flaggedSessions;

private string _smtpServerName;
private string _smtpFromAddress;
private string _smtpToAddress;
private int _smtpServerPort;
Step 3 - Merge the Initialize() methods

In this step you need to merge the Initialize() methods from the two walkthroughs so that all of the settings are retrieved from the IIS configuration file when the provider is loaded by the FTP service. In my provider this looked like the following:

// Initialize the provider.
protected override void Initialize(StringDictionary config)
{
    // Test if the application has already been initialized.
    if (_initialized == false)
    {
        // Create the flagged sessions list.
        _flaggedSessions = new List<string>();
        
        // Retrieve the connection string for the database connection.
        _connectionString = config["connectionString"];
        if (string.IsNullOrEmpty(_connectionString))
        {
            // Raise an exception if the connection string is missing or empty.
            throw new ArgumentException(
                "Missing connectionString value in configuration.");
        }
        else
        {
            // Determine whether the database is a Microsoft Access database.
            if (_connectionString.Contains("Microsoft.Jet"))
            {
                // Throw an exception if the database is a Microsoft Access database.
                throw new ProviderException("Microsoft Access databases are not supported.");
            }
        }
        
        // Retrieve the number of failures before an IP
        // address is locked out - or use the default value.
        if (int.TryParse(config["logonAttempts"], out _logonAttempts) == false)
        {
            // Set to the default if the number of logon attempts is not valid.
            _logonAttempts = defaultLogonAttempts;
        }
        
        // Retrieve the number of seconds for flood
        // prevention - or use the default value.
        if (int.TryParse(config["floodSeconds"], out _floodSeconds) == false)
        {
            // Set to the default if the number of logon attempts is not valid.
            _floodSeconds = defaultFloodSeconds;
        }
        
        // Test if the number is a positive integer and less than 10 minutes.
        if ((_floodSeconds <= 0) || (_floodSeconds > 600))
        {
            // Set to the default if the number of logon attempts is not valid.
            _floodSeconds = defaultFloodSeconds;
        }
        
        // Retrieve the email settings from configuration.
        _smtpServerName = config["smtpServerName"];
        _smtpFromAddress = config["smtpFromAddress"];
        _smtpToAddress = config["smtpToAddress"];
        
        // Detect and handle any mis-configured settings.
        if (!int.TryParse(config["smtpServerPort"], out _smtpServerPort))
        {
            _smtpServerPort = defaultSmtpPort;
        }
        if (string.IsNullOrEmpty(_smtpServerName))
        {
            throw new ArgumentException(
                "Missing smtpServerName value in configuration.");
        }
        if (string.IsNullOrEmpty(_smtpFromAddress))
        {
            throw new ArgumentException(
                "Missing smtpFromAddress value in configuration.");
        }
        if (string.IsNullOrEmpty(_smtpToAddress))
        {
            throw new ArgumentException(
                "Missing smtpToAddress value in configuration.");
        }
        
        // Initial garbage collection.
        GarbageCollection(true);
        
        // Flag the provider as initialized.
        _initialized = true;
    }
}
Step 4 - Add a SendEmail() method

For this step I copied some of my code from the email walkthrough and used it as the foundation for a new SendEmail() method that I added to the provider. In my provider this looked like the following:

private void SendEmail(string emailSubject, string emailMessage)
{
    // Create an SMTP message.
    SmtpClient smtpClient = new SmtpClient(_smtpServerName, _smtpServerPort);
    MailAddress mailFromAddress = new MailAddress(_smtpFromAddress);
    MailAddress mailToAddress = new MailAddress(_smtpToAddress);
    
    using (MailMessage mailMessage = new MailMessage(mailFromAddress, mailToAddress))
    {
        try
        {
            // Format the SMTP message as UTF8.
            mailMessage.BodyEncoding = Encoding.UTF8;
            // Add the subject.
            mailMessage.Subject = emailSubject;
            // Add the body.
            mailMessage.Body = emailMessage;
            // Send the email message.
            smtpClient.Send(mailMessage);
        }
        catch (SmtpException ex)
        {
            // Send an exception message to the debug
            // channel if the email fails to send.
            Debug.WriteLine(ex.Message);
        }
    }
}

Note: This uses the settings that you store in your IIS applicationHost.config file and are loaded by the Initialize() method.

Step 5 - Add email functionality to the BanAddress() method

In this step you add the functionality to send an email whenever an IP address is added to the list of banned IP addresses. In my provider this looked like the following:

// Mark an IP address as banned.
private void BanAddress(string ipAddress)
{
    // Check if the IP address is already banned.
    if (IsAddressBanned(ipAddress) == false)
    {
        // Ban the IP address if it is not already banned.
        InsertDataIntoTable("[BannedAddresses]",
            "[IPAddress]", "'" + ipAddress + "'");
        // Send an email for the banned address.
        SendEmail("Banned IP Address",
            "The IP address " + ipAddress + " was banned.");
    }
}
Step 6 - Methods that are not changed

I need to point out that there are several methods that require no changes. These methods are listed here for reference:

  • Dispose()
  • AuthenticateUser()
  • Log()
  • IsValidUser()
  • IsAddressBanned()
  • IsSessionFlagged()
  • FlagSession()
  • GarbageCollection
  • GetRecordCountByCriteria()
  • InsertDataIntoTable()
  • DeleteRecordsByCriteria()
  • ExecuteQuery()

Note: You could easily add the email functionality to the FlagSession() method so you will see when a banned IP address is trying to access your server, but depending on the number of sessions that are flagged on your server you might receive more emails than you really need.

Step 7 - Register the provider and configure your settings

In this last step you add the provider to your IIS configuration settings using the AppCmd utility, and you specify the values for the various settings that the provider requires:

cd %SystemRoot%\System32\Inetsrv

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"[name='FtpAddressRestrictionAuthentication',type='FtpAddressRestrictionAuthentication,FtpAddressRestrictionAuthentication,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpServerName',value='localhost']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpServerPort',value='25']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpFromAddress',value='someone@contoso.com']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpToAddress',value='someone@contoso.com']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='connectionString',value='Server=localhost;Database=FtpAuthentication;User ID=FtpLogin;Password=P@ssw0rd']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='logonAttempts',value='5']" /commit:apphost

AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='floodSeconds',value='30']" /commit:apphost

Note: You need to update the above syntax using the managed type information for your provider and the configuration settings for your SMTP server, email addresses, and database connection string.

Step 8 - Add the provider to a site

In this last step you add the provider to a site. If you were adding the provider to your Default Web Site that would look like the following:

AppCmd.exe set config -section:system.applicationHost/sites /"[name='Default Web Site'].ftpServer.security.authentication.basicAuthentication.enabled:False" /commit:apphost

AppCmd.exe set config -section:system.applicationHost/sites /+"[name='Default Web Site'].ftpServer.security.authentication.customAuthentication.providers.[name='FtpAddressRestrictionAuthentication',enabled='True']" /commit:apphost

AppCmd set site "Default Web Site" /+ftpServer.customFeatures.providers.[name='FtpAddressRestrictionAuthentication',enabled='true'] /commit:apphost

Summary

That wraps it up for today's post, and I hope you find it useful. Smile


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