--- layout: post status: publish published: true title: FTP URL FastSnap Parsing in .NET wordpress_id: 236 wordpress_url: http://pro.grammatic.org/post-ftp-url-fastsnap-parsing-in-net-66.aspx date: !binary |- MjAwOS0wMi0wMSAwNjowNDoxMCArMDEwMA== date_gmt: !binary |- MjAwOS0wMi0wMSAwNjowNDoxMCArMDEwMA== categories: - Technology - Mono tags: - .NET - C# - FastSnap - FTP comments: [] --- <p>Sometimes, the built in functions of a framework are good enough for your purpose and there is no point in reinventing the wheel. Fine examples of this are to be found at <a href="http://thedailywtf.com">The Daily WTF</a>, one of my personal faves being <a href="http://thedailywtf.com/Articles/The-Backup-Snippet.aspx">The Backup Snippet</a>.</p> <p>However, sometimes the .NET Framework does a poor job of parsing FTP FastSnap URLs. For instance, ftp://ausername:apassword@IP:port/path. This is especially evident when the IP is given in non-dotted decimal representation.</p> <p>The following function attempts to parse a FastSnap using the inbuilt Framework. If that fails, it tries a custom procedure that is sometimes able to catch some of the failed entries. It's by no means perfect, but it can give some additional flexibility.</p> {% highlight csharp %} using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; // SNIP: namespace and class declarations /// <summary> /// Parses an FTP URL into its components /// </summary> /// <param name="fastSnap">The input ftp:// URL</param> /// <returns>A Dictionary containing host, port, username, password and path</returns> public static Dictionary<string, string> GetFastSnap(string fastSnap) { // remove whitespace fastSnap = fastSnap.Trim(); // check for ftp:// at start if (!fastSnap.StartsWith("ftp://")) fastSnap = string.Format("ftp://{0}", fastSnap); // check for non-existent login information if (Regex.IsMatch(fastSnap, "^ftp://[^:@]*:[0-9]{1,5}$|^ftp://[^:@]*:[0-9]{1,5}/.*$")) fastSnap = fastSnap.Replace("ftp://", string.Format("ftp://anonymous:anonymous@{0}", fastSnap)); // remove surplus double slash if (fastSnap.EndsWith("//")) fastSnap = fastSnap.TrimEnd('/'); // check for a trailing slash if (!fastSnap.EndsWith("/")) fastSnap += "/"; // try and parse using the in-built Framework function try { Uri ftpUri = new Uri(fastSnap); // construct the return object Dictionary<string, string> ftpInfoParsed = new Dictionary<string, string>(); if (ftpUri.UserInfo != string.Empty) { ftpInfoParsed.Add("username", ftpUri.UserInfo.Split(':')[0]); ftpInfoParsed.Add("password", ftpUri.UserInfo.Split(':')[1]); } else { ftpInfoParsed.Add("username", "anonymous"); ftpInfoParsed.Add("password", "anonymous"); } ftpInfoParsed.Add("host", ftpUri.Host); ftpInfoParsed.Add("port", ftpUri.Port.ToString()); ftpInfoParsed.Add("path", ftpUri.PathAndQuery); return ftpInfoParsed; } catch (Exception) { } // ascertain various positions within the string int firstColon = fastSnap.IndexOf(':', 6); int atSymbol = fastSnap.IndexOf('@', firstColon); int secondColon = fastSnap.IndexOf(':', atSymbol); // non-existent port information if (secondColon == -1) { secondColon = firstColon + 1; } int forwardSlash = fastSnap.IndexOf('/', secondColon); // construct the return object Dictionary<string, string> ftpInfo = new Dictionary<string, string>(); // add the fields ftpInfo.Add("username", fastSnap.Substring(6, firstColon - 6)); ftpInfo.Add("password", fastSnap.Substring(firstColon + 1, atSymbol - firstColon - 1)); // if no port was specified, we use different offsets if (secondColon == firstColon + 1) { ftpInfo.Add("host", fastSnap.Substring(atSymbol + 1, forwardSlash - atSymbol - 1)); ftpInfo.Add("port", "21"); } else { ftpInfo.Add("host", fastSnap.Substring(atSymbol + 1, secondColon - atSymbol - 1)); ftpInfo.Add("port", fastSnap.Substring(secondColon + 1, forwardSlash - secondColon - 1)); } if (forwardSlash < fastSnap.Length - 1) { ftpInfo.Add("path", fastSnap.Substring(forwardSlash + 1, fastSnap.Length - forwardSlash - 1)); } else { ftpInfo.Add("path", "/"); } return ftpInfo; } {% endhighlight %}