Published February 1, 2009 | https://doi.org/10.59348/11rxw-mwh37

FTP URL FastSnap Parsing in .NET

Creators & Contributors

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 The Daily WTF, one of my personal faves being The Backup Snippet.

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.

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.

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;

        }

FTP URL FastSnap Parsing in .NET was originally published by Martin Paul Eve at eve.gd: Martin Paul Eve on February 01, 2009.

Files

10.59348_11rxw-mwh37.pdf

Files (139.3 kB)

Name Size Download all
md5:cd4e00d1bba339c595d465dfadf4f2e7
139.3 kB Preview Download

Additional details

Description

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 The Daily WTF, one of my personal faves being The Backup Snippet. However, sometimes the .NET Framework does a poor job of parsing FTP FastSnap URLs. For instance, ftp://ausername:apassword@IP:port/path.

Identifiers

GUID
https://doi.org/10.59348/11rxw-mwh37
URL
https://eve.gd/2009/02/01/ftp-url-fastsnap-parsing-in-net/

Dates

Issued
2009-02-01T00:00:00Z
Updated
2009-02-01T05:04:10Z