Create an FTP Provider that Sends an Email when Files are Uploaded

by Robert McMurray

Microsoft has created a new FTP service that has been completely rewritten for Windows ServerĀ® 2008. This new FTP service incorporates many new features that enable Web authors to publish content more easily than before, and offers Web administrators more security and deployment options.

The new FTP 7.5 service supports extensibility that lets you extend the built-in functionality that is included with the FTP service. More specifically, FTP 7.5 supports the creation of your own authentication providers. You can also create providers for custom FTP logging and for determining the home directory information for your FTP users.

FTP 7.5 extensibility does not have a request-processing pipeline like the HTTP process model in IIS 7.0 and above, but you can use the IFtpLogProvider.Log() method to implement custom actions in response to FTP commands that have been sent by FTP clients, and you can examine the FTP status code to determine whether the FTP command succeeded or failed.

Note

You do not have to have to actually write to a log file when you implement the IFtpLogProvider.Log() method in your providers.

As a practical illustration, this walkthrough will lead you through the steps to use managed code to create a simple FTP provider that sends an email whenever a file is uploaded, and changes the text of the email message depending on whether the upload succeeded or failed.

The FTP provider in this walkthrough implements the IFtpLogProvider.Log() method and uses the FtpLogEntry.Command and FtpLogEntry.FtpStatus values to determine the FTP command from the client and the response status from FTP service. In addition, the provider overrides the IFtpLogProvider.Initialize() method to retrieve the SMTP server name/port and email addresses for the email message from the provider settings in your provider's entry in the ApplicationHost.config file. If the SMTP settings have not been added to the configuration settings, the FTP provider will return a simple exception to the FTP service and do nothing. You can monitor for these exceptions using ETW tracing, or you could add Debug.WriteLine() statements to write that status to the debug channel.

When an FTP client sends a file to the FTP service, the client will send a STOR command to the server. The FTP server should respond with a 226 status if the command succeeded, or a different status if the command failed. For example, the FTP service might return a 550 status if the user's disk quota has been exceeded.

Prerequisites

The following items are required to complete the procedures in this article:

  1. IIS 7.0 or above must be installed on your Windows Server 2008 server, and the Internet Information Services (IIS) Manager must also be installed.

  2. The new FTP 7.5 service must be installed. You can download and install the FTP 7.5 service from the https://www.iis.net/ web site using one of the following links:

  3. You must use Visual Studio 2008.

    Note

    If you use an earlier version of Visual Studio, some of the steps in this walkthrough may not be correct.

Step 1: Set up the Project Environment

In this step, you will create a project in Visual Studio 2008 for the demo provider.

  1. Open Microsoft Visual Studio 2008.

  2. Click the File menu, then New, then Project.

  3. In the New Project dialog box:

    • Choose Visual C# as the project type.
    • Choose Class Library as the template.
    • Type FtpMailDemo as the name of the project.
    • Click OK.
  4. When the project opens, add a reference path to the FTP extensibility library:

    • Click Project, and then click FtpMailDemo Properties.

    • Click the Reference Paths tab.

    • Enter the path to the FTP extensibility assembly for your version of Windows, where C: is your operating system drive:

      • For Windows Server 2008 and Windows Vista:

        C:\Windows\assembly\GAC_MSIL\Microsoft.Web.FtpServer\7.5.0.0__31bf3856ad364e35
        
      • For Windows 7:

        C:\Program Files\Reference Assemblies\Microsoft\IIS
        
    • Click Add Folder.

  5. Add a strong name key to the project:

    • Click Project, and then click FtpMailDemo Properties.
    • Click the Signing tab.
    • Check the Sign the assembly check box.
    • Choose <New...> from the strong key name drop-down box.
    • Enter FtpMailDemoKey for the key file name.
    • If desired, enter a password for the key file; otherwise, clear the Protect my key file with a password check box.
    • Click OK.
  6. Optional: You can add a custom build event to add the DLL automatically to the Global Assembly Cache (GAC) on your development computer:

    • Click Project, and then click FtpMailDemo Properties.

    • Click the Build Events tab.

    • Enter the following in the Post-build event command line dialog box:

      net stop ftpsvc
      call "%VS90COMNTOOLS%\vsvars32.bat">null
      gacutil.exe /if "$(TargetPath)"
      net start ftpsvc
      
  7. Save the project.

Step 2: Create the Extensibility Class

In this step, you will implement the extensibility interface for the demo provider.

  1. Add a reference to the FTP extensibility library for the project:

    • Click Project, and then click Add Reference...
    • On the .NET tab, click Microsoft.Web.FtpServer.
    • Click OK.
  2. Add a reference to System.Web for the project:

    • Click Project, and then click Add Reference...
    • On the .NET tab, click System.Web.
    • Click OK.
  3. Add the code for the authentication class:

    • In Solution Explorer, double-click the Class1.cs file.

    • Remove the existing code.

    • Paste the following code into the editor:

      using System;
      using System.Collections.Specialized;
      using System.Diagnostics;
      using System.Net.Mail;
      using System.Text;
      using Microsoft.Web.FtpServer;
      
      public sealed class FtpMailDemo : BaseProvider, IFtpLogProvider
      {
        private string smtpServerName;
        private string smtpFromAddress;
        private string smtpToAddress;
        private int smtpServerPort;
      
        // Override the default initialization method.
        protected override void Initialize(StringDictionary config)
        {
          // Retrieve the provider 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 = 25;
          }
          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.");
          }
        }
      
        // Implement the logging method.
        void IFtpLogProvider.Log(FtpLogEntry loggingParameters)
        {
          // Test for a file upload operation.
          if (loggingParameters.Command == "STOR")
          {
            // 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))
            {
              // Format the SMTP message as UTF8.
              mailMessage.BodyEncoding = Encoding.UTF8;
              // Test for a successful operation.
              if (loggingParameters.FtpStatus == 226)
              {
                // Create a customized message for a successful operation.
                mailMessage.Subject = "File Uploaded Successfully";
                mailMessage.Body = loggingParameters.UserName +
                  " successfully uploaded a file to " +
                  loggingParameters.FullPath;
              }
              else
              {
                // Create a customized message for a failed operation.
                mailMessage.Subject = "File Operation Status";
                mailMessage.Body =
                  "The FTP service returned a status of " +
                  loggingParameters.FtpStatus + "." +
                  loggingParameters.FtpSubStatus +
                  " when " + loggingParameters.UserName +
                  " attempted to upload a file to " +
                  loggingParameters.FullPath;
              }
              try
              {
                // 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.ToString());
              }
            }
          }
        }
      }
      
  4. Save and compile the project.

Note

If you did not use the optional steps to register the assemblies in the GAC, you will need to manually copy the assemblies to your IIS computer and add the assemblies to the GAC using the Gacutil.exe tool. For more information, see the Gacutil.exe (Global Assembly Cache Tool) article.

Step 3: Add the Custom Provider to FTP

In this step, you will add the demo provider to your FTP service and an FTP site.

  1. Determine the assembly information for the extensibility provider:

    • In Windows Explorer, open your C:\Windows\assembly path, where C: is your operating system drive.
    • Locate the FtpMailDemo assembly.
    • Right-click the assembly, and then click Properties.
    • Copy the Culture value; for example: Neutral.
    • Copy the Version number; for example: 1.0.0.0.
    • Copy the Public Key Token value; for example: 426f62526f636b73.
    • Click Cancel.
  2. Add the extensibility provider to the global list of FTP providers:

    • At the moment there is no UI that enables you to add a custom provider with its related configuration settings to IIS, so you will have to use command line syntax like the following example:

      appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"[name='FtpMailDemo',type='FtpMailDemo,FtpMailDemo,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73']" /commit:apphost
      
      appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpMailDemo']" /commit:apphost
      
      appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpMailDemo'].[key='smtpServerName',value='localhost']" /commit:apphost
      
      appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpMailDemo'].[key='smtpServerPort',value='25']" /commit:apphost
      
      appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpMailDemo'].[key='smtpFromAddress',value='someone@contoso.com']" /commit:apphost
      
      appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpMailDemo'].[key='smtpToAddress',value='someone@contoso.com']" /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 and email addresses.

  3. Add the custom provider to a site:

    • At the moment there is no UI that enables you to add custom features to a site, so you will have to use command line syntax like the following example that configures the Default Web Site:

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

Summary

In this walkthrough you learned how to:

  • Create a project in Visual Studio 2008 for a custom FTP provider.
  • Implement the extensibility interface for custom FTP functionality.
  • Add a custom provider to your FTP service.

When users attempt to upload files to your FTP site, the FTP service will send an email message with a success or failure message using the SMTP server settings that you specified in your provider definition.