How to create an FTP client in C#

Creating an FTP client in a CLR compliant languge such as C# is truly a breeze compared to native languages like C or C++. The .NET Framework class library hands you the useful classes of System.Net on a platter which you can use to write your FTP functionality without worrying about things like memory allocation, protocol handling, etc. The .NET API does that on your behalf!

Before writing the code, it is useful to sit down and give a thought to your program design. Do you want to create a library to reuse in a larger project, or just a Windows app that acts as an FTP client? If you are a student doing some test project, I bet its the latter you will prefer! However, object-oriented programming and the concept of object-reuse dictate that you create a library first and then proceed with building your GUI, though it takes a little bit of extra effort.

In this tutorial I’ll briefly explain the functionality of libftp, an FTP library I have created in C# which I can seamlessly integrate into any application I develop or distribute it as a class-library/DLL. I also built a proof-of-concept Windows Forms application along with, to ensure the new functionality is working well. The library and application are available as open-source project on codeplex. You can browse through the code, understand it and straight away plug-it in your .NET application 100% free of cost.

The workings of this library is simplicity itself. There are just two classes – ftper class that acts as an interface to this library. It exposes public methods like connect(), disconnect(), browse(), addFilesToDownloadQueue(), etc. Here are some implementation details:

public bool isProcessing()
{
    return _threadRunning; //check if a thread is running for up/download
}

public List<ftpinfo> connect(string host, string username, string password)
{
    return ftpobject.connect(host,username,password);
}

public void disconnect()
{
    if (_threadRunning)
    {
        _threadRunning=false;
    }

    int timeout=60; //seconds
    DateTime start=DateTime.Now;
    while(queue.Count==0) //wait till running up/download threads complete.
    {
        if (DateTime.Now.Subtract(start).Seconds>timeout)
            break;
    }
}

public List<ftpinfo> browse(string path)
{
    return ftpobject.browse(path);
}

The second class, ftp is the core class which does the actual ftp stuff by connecting to the remote-host and transferring files. It is the underlying class for ftpobject in the above code.  It also exposes some events like uploadComplete and downloadComplete that our client can subscribe to, so that he/she can get notified of these events.


internal ftp() //hide constructor from the outside world.
{
context = SynchronizationContext.Current;
}

//tests connection and browse to home-directory
 public List<ftpinfo> connect(string host,string username,string password)
 {
 this._username=username;
 this._password=password;

//FtpWebRequest.Create(host); //test connect;
 context = SynchronizationContext.Current;

return browse(host);
 }

//public bool exists(string remotefilename)
 //{
 //    return true;
 //}

/// <summary>
 /// Fetch all files/folders in this directory and return the ftpinfo array.
 /// </summary>
 public List<ftpinfo> browse(string path) //eg: "ftp.xyz.org", "ftp.xyz.org/ftproot/etc"
 {
 FtpWebRequest request=(FtpWebRequest)FtpWebRequest.Create(path);
 request.Method=WebRequestMethods.Ftp.ListDirectoryDetails;
 List<ftpinfo> files=new List<ftpinfo>();

//request.Proxy = System.Net.WebProxy.GetDefaultProxy();
 //request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
 request.Credentials = new NetworkCredential(_username, _password);
 Stream rs=(Stream)request.GetResponse().GetResponseStream();

OnStatusChange("CONNECTED: " + path, 0, 0);

StreamReader sr = new StreamReader(rs);
 string strList = sr.ReadToEnd();
 string[] lines=null;

if (strList.Contains("\r\n"))
 {
 lines=strList.Split(new string[] {"\r\n"},StringSplitOptions.None);
 }
 else if (strList.Contains("\n"))
 {
 lines=strList.Split(new string[] {"\n"},StringSplitOptions.None);
 }

//now decode this string array

if (lines==null || lines.Length == 0)
 return null;

foreach(string line in lines)
 {
 if (line.Length==0)
 continue;
 //parse line
 Match m= GetMatchingRegex(line);
 if (m==null) {
 //failed
 throw new ApplicationException("Unable to parse line: " + line);
 }

ftpinfo item=new ftpinfo();
 item.filename = m.Groups["name"].Value.Trim('\r');
 item.path = path;
 item.size = Convert.ToInt64(m.Groups["size"].Value);
 item.permission = m.Groups["permission"].Value;
 string _dir = m.Groups["dir"].Value;
 if(_dir.Length>0  && _dir != "-")
 {
 item.fileType = directoryEntryTypes.directory;
 }
 else
 {
 item.fileType = directoryEntryTypes.file;
 }

try
 {
 item.fileDateTime = DateTime.Parse(m.Groups["timestamp"].Value);
 }
 catch
 {
 item.fileDateTime = DateTime.MinValue; //null;
 }

files.Add(item);
 }

return files;
 }

The reason I’ve kept the constructor method ftp() internal is that we don’t want our client creating these objects as they form the core ftp functionality that only our library is supposed to use. By using the keyword “internal”, C# compiler understands that we don’t want this consturctor to be visible outside our assembly.

List of development tools for Linux platform – IDEs, Compilers, etc.

One of my most recent endeavours was to set up my old linux machine for development. I chose the Debian Squeeze(6.1.5) distro because of its reputation for stability and also its minimalistic approach towards installing packages, and both these features were quite welcome. Having said that, the tools listed here can be downloaded and installed on any distro, in case they are not already included in the installation CD/DVD. Based on my development experience with these tools, this list is frequently updated with new information:

1. GCC (GNU Compiler Collection): The GCC toolchain is considered a sin-qua-non of any linux developer’s toolbox. In fact, the linux kernel itself relies on several libraries provided as part of GCC. The debian squeeze 6.1.5 includes GCC 4.4 which is pretty stable. I’ve compiled several programs in C/C++ without any issues. You can choose to install specific packages such as gcc4.4 for C, or g++4.4 for the C++ language.

Only issue is that you should not install the OpenJDK package (gcj) of the GNU collection. The reason being that it will clash with the Sun/Oracle Java version which is very much preferred if you are into Android development or use other Java features such as Swing or AWT.

GCC is one of your core tools. Whether you use Eclipse or Netbeans to code your C/C++ programs, whether you use the QTcreator or Glade for designing interfaces, GCC is one good toolchain that most IDEs rely on to build your application.

2. Java/Netbeans: You can download all the java editions for linux including Java ME/EE, documentation & samples and also the Netbeans package from here. I specifically chose the combined Netbeans+JDK7.5 to avoid getting Netbeans separately.

3. MonoDevelop: One of the most important tools that allows me to leverage my Microsoft VB.NET/C# skills on linux is the MonoDevelop IDE. Except for Microsoft proprietary classes such as System.Windows.Forms, all your .NET code is 100% portable to linux through the Mono platform. The only support that MonoDevelop lacks as of now is the ability to design ASP.NET web pages. I believe this limitation is going to be overcome pretty soon.

4. Eclipse: Eclipse was essential for me as I wanted to develop Android apps too which is easier using the Eclipse IDE. The debian squeeze CD comes with version 3.5.2 of eclipse package, whereas I wanted to try the latest Juno version, so I got it from the eclipse site. However, the eclipse SDK for linux is a tar package and not an installer package. This means you have to extract this tar separately in a folder and use eclipse from there. If you do so, ensure that the ..JDK/bin is set in your $PATH variable, so that you can invoke java from the command line. If not, then add it by editing the .profile file in your home directory (/home/xyz, etc..).

5. Android Suite: The android developer site contains complete information about how to download and install the android sdk, along with the system images for the particular android platforms for which you wish to develop. Just keep in mind that android needs the Sun/Oracle java sdk and not the OpenJDK provided by GNU to write android programs.

6. Glade: Glade is an excellent RAD tool to rapidly develop professional-looking user interfaces for your applications. Glade is based on the Gtk+ toolkit that forms the core of the GNOME desktop. Glade basically generates an xml file (similar to the XAML generated in visual-studio) that can be used in many languages such as  C, C++ and Java. However, since the Gtk+ framework is built in C, it is advisable to build your GNOME applications in C for performance benefits. Glade can also be integrated with Anjuta, a full fledged IDE to develop GNOME applications that includes full toolchain integration and debugging support.

7. QTcreator: Based on the extensive QT framework, QTcreator is to KDE desktop what Glade is to GNOME. However, don’t feel crippled because of this as everything is “mix-and-match-able” in the linux world. QT apps can run on GNOME, as do Gtk+ apps run on KDE desktop. Since QT framework is written in C++, the language is well suited to write QT apps. Qtcreator can also be integrated with KDevelop, the equivalent of Anjuta IDE on KDE.

Solution for issues with Intel 845g graphic cards on Linux

Recently, I installed Debian Squeeze (6.0.5) on my old Zenith machine with a bit modest specs (384 MB RAM & 40GB HDD). It also had an Intel Extreme graphics 82845g for a video card.

While using the system I faced one major annoyance that in retrospect, was traced to my video card. My machine used to suddenly hang at random intervals.  When I switched to tty1 by pressing ctrl+alt+F1, I saw something like this:

X.Org X Server 1.7.7
Release Date: 2010-05-04
X Protocol Version 11, Revision 0
Build Operating System: Linux 2.6.32-5-amd64 i686 Debian
Current Operating System: Linux workstation1 2.6.32-5-686 #1 SMP Sun May 6 04:01:19........
Kernel Command Line....
...
...
..
FATAL: Module fbcon not found.
SELinux: Disabled on system, not enabling in X server
(EE) Intel(0): Detected a hung GPU, disabling acceleration.

After a bit of googling, I found out that this behaviour was quite usual for most Intel cards in the i8xx series that included my card too. Turns out that Intel has not released the complete specs for this card that are needed to develop an open-source driver. As a result of this, all Windows machines will run in full acceleration with Intel’s proprietary drivers, whereas the Linux folk have to find an alternative or suffer these hangs/crashes. You can find the notes from the developer of Xorg’s driver here.

If you are facing this same problem, there are two workarounds available. I’ve tested these on debian squeeze and they both worked for me. Hopefully one of them should work for you.

Workaround [1]: Use the generic vesa driver instead of the intel driver. You may not get the cutting-edge graphics on your screen, but you will get a more robust machine. If you choose to do this step, you will also have to disable kms (kernel mode setting) as vesa does not support it. If you decide to go for this step, here is how you go about doing it:

1. First locate your /etc/X11/xorg.conf file and open in your text editor. You can find your driver configuration in the device section somewhere like:

….

Driver      “intel”
VendorName  “Intel Corporation”
BoardName   “82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device”
BusID       “PCI:0:2:0”

…..

If there is no xorg.conf file in /etc/X11 folder, then you will have to generate one. To do this, log-in as root on command prompt and perform these steps (make sure that no x-session is running on a terminal when you do this):

I.) Xorg -configure               (this will generate the xorg.conf file)

II.) mv /root/xorg.new.conf /etc/X11/xorg.conf

2. Now that you have located the driver section, you can change it by simply replacing “intel” with “vesa”. To do this change, again you have to login as root on the command prompt. You can use your default console editor such as vi or nano for this.

3. The last step is to disable kms (kernel mode setting) for the vesa driver. To do this edit the file /etc/modprobe.d/i915-kms.conf and replace modeset=1 with modeset=0.

4. Reboot your machine and test the new display.

Workaround [2]: This alternative workaround is also available if you are intent on using the intel driver instead of the vesa one. However, the stability is not as much guaranteed as the vesa solution, and you might have to do some experimenting. To do this, just edit the /etc/X11/xorg.conf while logging as a root on the command prompt (If the xorg.conf file does not exist, see how to generate it in Workaround-1). Uncomment some driver options in your device section and set those values as follows:

Section “Device”
### Available Driver options are:-
### Values: <i>: integer, <f>: float, <bool>: “True”/”False”,
### <string>: “String”, <freq>: “<f> Hz/kHz/MHz”
### [arg]: arg optional
        Option     “AccelMethod” “uxa”           # [<str>]
        Option     “DRI”        “false”            # [<bool>]
#Option     “ColorKey”               # <i>
#Option     “VideoKey”               # <i>
#Option     “FallbackDebug”          # [<bool>]
        Option     “Tiling” “false”                 # [<bool>]
        Option     “Shadow”     “true”                # [<bool>]
#Option     “SwapbuffersWait”        # [<bool>]
#Option     “XvMC”                   # [<bool>]
#Option     “XvPreferOverlay”        # [<bool>]
#Option     “DebugFlushBatches”      # [<bool>]
#Option     “DebugFlushCaches”       # [<bool>]
#Option     “DebugWait”              # [<bool>]
    Option    “MigrationHeuristic” “greedy”
Identifier  “Card0”
Driver      “intel”
VendorName  “Intel Corporation”
BoardName   “82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device”
BusID       “PCI:0:2:0”
EndSection

Reboot your machine after saving xorg.conf. The bold options above are those that worked for me. Just remember that setting Shadow to true and DRI to false will work in most cases, so just try setting those two options first.

EDIT: As of today (18-07-2012), it seems that the issue is permanently resolved in the next debian release (testing/wheezy).  I installed the backports packages for xorg and the newer kernel version (3.2) and the issue resolved even with the default xorg.conf configuration.