Thursday, July 14, 2011

FTPS also known as FTP over SSL example with C# and libCURL (libcurl.NET)


Background

Two separate methods were developed to invoke client security for use with FTP clients: Explicit or Implicit. The explicit method is a legacy compatible implementation where FTPS aware clients can invoke security with an FTPS aware server without breaking overall FTP functionality with non-FTPS aware clients. The implicit method requires that all clients of the FTPS server be aware that SSL is to be used on the session, and thus is incompatible with non-FTPS-aware clients.

- Wikipedia (link to Wikipedia FTPS article)

Download libcurl.NET

Go to libcurl.NET home page and download library (link to file download).

Create and setup new solution

Your solution should look like this:


Extract and copy libcurl.NET files to your solution libs directory should get something like this:


Add LibCurlNet refernce from libcurl.NET files bin directory to your project.

Copy libcurl.dll, LibCurlShim.dll and ca-bundle.crt files from libcurl.NET files bin directory to your project root directory. Add libcurl.dll, LibCurlShim.dll and ca-bundle.crt files to project tree and set their properties to 'Copy if newer' value.

Add the following source code to your Program.cs file Main method:

Source code

using System;
using System.Collections.Generic;
using SeasideResearch.LibCurlNet;

namespace libcurlFtpsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("libcurlFtpsExample...");

            const string url = "ftp://user:thepassword@ftp.somesite.com/dir/";

            try
            {
                Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);

                Easy easy = new Easy();
                if (easy != null)
                {
                    easy.SetOpt(CURLoption.CURLOPT_URL, url);
                    easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, false);
                    easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYHOST, false);
                    easy.SetOpt(CURLoption.CURLOPT_FTP_SSL, CURLftpSSL.CURLFTPSSL_TRY);

                    // For debugging will print headers to console.
                    Easy.HeaderFunction hf = new Easy.HeaderFunction(OnHeaderData);
                    easy.SetOpt(CURLoption.CURLOPT_HEADERFUNCTION, hf);
                    
                    // For debugging will print received data to console.
                    Easy.DebugFunction df = new Easy.DebugFunction(OnDebug);
                    easy.SetOpt(CURLoption.CURLOPT_DEBUGFUNCTION, df);
                    easy.SetOpt(CURLoption.CURLOPT_VERBOSE, true);
     
                    // List directory only
                    easy.SetOpt(CURLoption.CURLOPT_FTPLISTONLY, true);
         
                    CURLcode code = easy.Perform();
                    if (code != CURLcode.CURLE_OK)
                    {
                        Console.WriteLine("Request failed!");
                    }

                    easy.Cleanup();
                }
                else
                {
                    Console.WriteLine("Failed to get Easy libcurl handle!");
                }
            }
            catch (Exception exp)
            {
                Console.WriteLine(exp);
            }
            finally
            {
                Curl.GlobalCleanup();
            }
        }

        public static void OnDebug(CURLINFOTYPE infoType, String msg, Object extraData)
        {
            // print out received data only
            if (infoType == CURLINFOTYPE.CURLINFO_DATA_IN)
                Console.WriteLine(msg);
        }

        public static Int32 OnHeaderData(Byte[] buf, Int32 size, Int32 nmemb, Object extraData)
        {
            Console.Write(System.Text.Encoding.UTF8.GetString(buf));
                return size * nmemb;
        }
    }
}


And here is output from program:


Excercise
What about file download instead of directory listing?

Well comment out line no. 37

easy.SetOpt(CURLoption.CURLOPT_FTPLISTONLY, true);

and replace it with this code:

Easy.WriteFunction wf = new Easy.WriteFunction(OnWriteData);
easy.SetOpt(CURLoption.CURLOPT_WRITEFUNCTION, wf);

and add implementation of OnWriteData static method in 'Program' class like this:

// only prints file context to console
public static Int32 OnWriteData(Byte[] buf, Int32 size, Int32 nmemb, Object extraData)
{
    Console.Write(System.Text.Encoding.UTF8.GetString(buf));
    return size * nmemb;
}

Also add your file at the end of url like this:

const string url = "ftp://user:thepassword@ftp.somesite.com/dir/yourfile.txt";

Done ;)