)>}]
شركة التطبيقات المتكاملة لتصميم وبرمجة البرمجيات الخاصة ش.ش.و.
Integrated Applications Programming Company
Skip Navigation LinksHome » Code Library » Ftp

Public general use code classes and xml files that we've compiled and used over the years:

A wrapper class for .NET 2.0 FTP

    1: using System;
    2: using System.Collections.Generic;
    3: using System.IO;
    4: using System.Net;
    5: using System.Text.RegularExpressions;
    6:  
    7: namespace Ia.Cl.Model
    8: {
    9:     /// <summary publish="true">
   10:     /// A wrapper class for .NET 2.0 FTP
   11:     /// </summary>
   12:     /// <remarks> 
   13:     /// This class does not hold open an FTP connection but instead is stateless: for each FTP request it connects, performs the request and disconnects.
   14:     /// </remarks>
   15:     /// 
   16:     /// <remarks> 
   17:     /// Copyright � 2001-2015 Jasem Y. Al-Shamlan (info@ia.com.kw), Integrated Applications - Kuwait. All Rights Reserved.
   18:     ///
   19:     /// This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
   20:     /// the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
   21:     ///
   22:     /// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   23:     /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   24:     /// 
   25:     /// You should have received a copy of the GNU General Public License along with this library. If not, see http://www.gnu.org/licenses.
   26:     /// 
   27:     /// Copyright notice: This notice may not be removed or altered from any source distribution.
   28:     /// </remarks>
   29:     public class Ftp
   30:     {
   31:         /// <summary>
   32:         /// Blank constructor
   33:         /// </summary>
   34:         /// <remarks> Hostname, username and password must be set manually</remarks>
   35:  
   36:         public Ftp()
   37:         {
   38:         }
   39:  
   40:         ////////////////////////////////////////////////////////////////////////////
   41:  
   42:         /// <summary>
   43:         /// Constructor just taking the hostname
   44:         /// </summary>
   45:         /// <param name="Hostname">in either ftp://ftp.host.com or ftp.host.com form</param>
   46:         /// <remarks> 
   47:         /// </remarks>
   48:         public Ftp(string Hostname)
   49:         {
   50:             _hostname = Hostname;
   51:         }
   52:  
   53:         ////////////////////////////////////////////////////////////////////////////
   54:  
   55:         /// <summary>
   56:         /// Constructor taking hostname, username and password
   57:         /// </summary>
   58:         /// <param name="Hostname">in either ftp://ftp.host.com or ftp.host.com form</param>
   59:         /// <param name="Username">Leave blank to use 'anonymous' but set password to your email</param>
   60:         /// <param name="Password"></param>
   61:         /// <remarks> </remarks>
   62:         public Ftp(string Hostname, string Username, string Password)
   63:         {
   64:             _hostname = Hostname;
   65:             _username = Username;
   66:             _password = Password;
   67:         }
   68:  
   69:         ////////////////////////////////////////////////////////////////////////////
   70:  
   71:         /// <summary>
   72:         /// Return a simple directory listing
   73:         /// </summary>
   74:         /// <param name="directory">Directory to list, e.g. /pub</param>
   75:         /// <returns>A list of filenames and directories as a List(of String)</returns>
   76:         /// <remarks> For a detailed directory listing, use ListDirectoryDetail</remarks>
   77:         public List<string> ListDirectory(string directory)
   78:         {
   79:             //return a simple list of filenames in directory
   80:             System.Net.FtpWebRequest ftp = GetRequest(GetDirectory(directory));
   81:             //Set request to do simple list
   82:             ftp.Method = System.Net.WebRequestMethods.Ftp.ListDirectory;
   83:  
   84:             string str = GetStringResponse(ftp);
   85:             //replace CRLF to CR, remove last instance
   86:             str = str.Replace("\r\n", "\r").TrimEnd('\r');
   87:             //split the string into a list
   88:             List<string> result = new List<string>();
   89:             result.AddRange(str.Split('\r'));
   90:             return result;
   91:         }
   92:  
   93:         ////////////////////////////////////////////////////////////////////////////
   94:  
   95:         /// <summary>
   96:         /// Return a detailed directory listing
   97:         /// </summary>
   98:         /// <param name="directory">Directory to list, e.g. /pub/etc</param>
   99:         /// <returns>An FtpDirectory object</returns>
  100:         public FtpDirectory ListDirectoryDetail(string directory)
  101:         {
  102:             System.Net.FtpWebRequest ftp = GetRequest(GetDirectory(directory));
  103:             //Set request to do simple list
  104:             ftp.Method = System.Net.WebRequestMethods.Ftp.ListDirectoryDetails;
  105:  
  106:             string str = GetStringResponse(ftp);
  107:             //replace CRLF to CR, remove last instance
  108:             str = str.Replace("\r\n", "\r").TrimEnd('\r');
  109:             //split the string into a list
  110:             return new FtpDirectory(str, _lastDirectory);
  111:         }
  112:  
  113:         ////////////////////////////////////////////////////////////////////////////
  114:  
  115:         /// <summary>
  116:         /// Copy a local file to the FTP server
  117:         /// </summary>
  118:         /// <param name="localFilename">Full path of the local file</param>
  119:         /// <param name="targetFilename">Target filename, if required</param>
  120:         /// <returns></returns>
  121:         /// <remarks> If the target filename is blank, the source filename is used
  122:         /// (assumes current directory). Otherwise use a filename to specify a name
  123:         /// or a full path and filename if required.</remarks>
  124:         public bool Upload(string localFilename, string targetFilename)
  125:         {
  126:             //1. check source
  127:             if (!File.Exists(localFilename))
  128:             {
  129:                 throw (new ApplicationException("File " + localFilename + " not found"));
  130:             }
  131:             //copy to FI
  132:             FileInfo fi = new FileInfo(localFilename);
  133:             return Upload(fi, targetFilename);
  134:         }
  135:  
  136:         ////////////////////////////////////////////////////////////////////////////
  137:  
  138:         /// <summary>
  139:         /// Upload a local file to the FTP server
  140:         /// </summary>
  141:         /// <param name="fi">Source file</param>
  142:         /// <param name="targetFilename">Target filename (optional)</param>
  143:         /// <returns></returns>
  144:         public bool Upload(FileInfo fi, string targetFilename)
  145:         {
  146:             //copy the file specified to target file: target file can be full path or just filename (uses current dir)
  147:  
  148:             //1. check target
  149:             string target;
  150:             if (targetFilename.Trim() == "")
  151:             {
  152:                 //Blank target: use source filename & current dir
  153:                 target = this.CurrentDirectory + fi.Name;
  154:             }
  155:             else if (targetFilename.Contains("/"))
  156:             {
  157:                 //If contains / treat as a full path
  158:                 target = AdjustDir(targetFilename);
  159:             }
  160:             else
  161:             {
  162:                 //otherwise treat as filename only, use current directory
  163:                 target = CurrentDirectory + targetFilename;
  164:             }
  165:  
  166:             string URI = Hostname + target;
  167:             //perform copy
  168:             System.Net.FtpWebRequest ftp = GetRequest(URI);
  169:  
  170:             //Set request to upload a file in binary
  171:             ftp.Method = System.Net.WebRequestMethods.Ftp.UploadFile;
  172:             ftp.UseBinary = true;
  173:  
  174:             //Notify FTP of the expected size
  175:             ftp.ContentLength = fi.Length;
  176:  
  177:             //create byte array to store: ensure at least 1 byte!
  178:             const int BufferSize = 2048;
  179:             byte[] content = new byte[BufferSize - 1 + 1];
  180:             int dataRead;
  181:  
  182:             //open file for reading
  183:             using (FileStream fs = fi.OpenRead())
  184:             {
  185:                 //open request to send
  186:                 using (Stream rs = ftp.GetRequestStream())
  187:                 {
  188:                     do
  189:                     {
  190:                         dataRead = fs.Read(content, 0, BufferSize);
  191:                         rs.Write(content, 0, dataRead);
  192:                     } while (!(dataRead < BufferSize));
  193:  
  194:                     //rs.Close();
  195:                 }
  196:             }
  197:  
  198:             ftp = null;
  199:  
  200:             return true;
  201:         }
  202:  
  203:         ////////////////////////////////////////////////////////////////////////////
  204:  
  205:         /// <summary>
  206:         /// Copy a file from FTP server to local
  207:         /// </summary>
  208:         /// <param name="sourceFilename">Target filename, if required</param>
  209:         /// <param name="localFilename">Full path of the local file</param>
  210:         /// <param name="PermitOverwrite"></param>
  211:         /// <returns></returns>
  212:         /// <remarks> Target can be blank (use same filename), or just a filename
  213:         /// (assumes current directory) or a full path and filename</remarks>
  214:         public bool Download(string sourceFilename, string localFilename, bool PermitOverwrite)
  215:         {
  216:             //2. determine target file
  217:             FileInfo fi = new FileInfo(localFilename);
  218:             return this.Download(sourceFilename, fi, PermitOverwrite);
  219:         }
  220:  
  221:         ////////////////////////////////////////////////////////////////////////////
  222:  
  223:         /// <summary>
  224:         ///
  225:         /// </summary>
  226:         //Version taking an FtpFileInfo
  227:         public bool Download(FtpFileInfo file, string localFilename, bool PermitOverwrite)
  228:         {
  229:             return this.Download(file.FullName, localFilename, PermitOverwrite);
  230:         }
  231:  
  232:         ////////////////////////////////////////////////////////////////////////////
  233:  
  234:         /// <summary>
  235:         ///
  236:         /// </summary>
  237:         //Another version taking FtpFileInfo and FileInfo
  238:         public bool Download(FtpFileInfo file, FileInfo localFI, bool PermitOverwrite)
  239:         {
  240:             return this.Download(file.FullName, localFI, PermitOverwrite);
  241:         }
  242:  
  243:         ////////////////////////////////////////////////////////////////////////////
  244:  
  245:         /// <summary>
  246:         ///
  247:         /// </summary>
  248:         //Version taking string/FileInfo
  249:         public bool Download(string sourceFilename, FileInfo targetFI, bool PermitOverwrite)
  250:         {
  251:             //1. check target
  252:             if (targetFI.Exists && !(PermitOverwrite))
  253:             {
  254:                 throw (new ApplicationException("Target file already exists"));
  255:             }
  256:  
  257:             //2. check source
  258:             string target;
  259:             if (sourceFilename.Trim() == "")
  260:             {
  261:                 throw (new ApplicationException("File not specified"));
  262:             }
  263:             else if (sourceFilename.Contains("/"))
  264:             {
  265:                 //treat as a full path
  266:                 target = AdjustDir(sourceFilename);
  267:             }
  268:             else
  269:             {
  270:                 //treat as filename only, use current directory
  271:                 target = CurrentDirectory + sourceFilename;
  272:             }
  273:  
  274:             string URI = Hostname + target;
  275:  
  276:             //3. perform copy
  277:             System.Net.FtpWebRequest ftp = GetRequest(URI);
  278:  
  279:             //Set request to download a file in binary mode
  280:             ftp.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
  281:             ftp.UseBinary = true;
  282:  
  283:             //open request and get response stream
  284:             using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
  285:             {
  286:                 using (Stream responseStream = response.GetResponseStream())
  287:                 {
  288:                     //loop to read & write to file
  289:                     using (FileStream fs = targetFI.OpenWrite())
  290:                     {
  291:                         byte[] buffer = new byte[2048];
  292:                         int read = 0;
  293:                         do
  294:                         {
  295:                             read = responseStream.Read(buffer, 0, buffer.Length);
  296:                             fs.Write(buffer, 0, read);
  297:                         } while (!(read == 0));
  298:  
  299:                         //responseStream.Close();
  300:                         //fs.Flush();
  301:                         //fs.Close();
  302:                     }
  303:  
  304:                     //responseStream.Close();
  305:                 }
  306:  
  307:                 //response.Close();
  308:             }
  309:  
  310:             return true;
  311:         }
  312:  
  313:         ////////////////////////////////////////////////////////////////////////////
  314:  
  315:         /// <summary>
  316:         /// Delete remote file
  317:         /// </summary>
  318:         /// <param name="filename">filename or full path</param>
  319:         /// <returns></returns>
  320:         /// <remarks> </remarks>
  321:         public bool FtpDelete(string filename)
  322:         {
  323:             //Determine if file or full path
  324:             string URI = this.Hostname + GetFullPath(filename);
  325:  
  326:             System.Net.FtpWebRequest ftp = GetRequest(URI);
  327:             //Set request to delete
  328:             ftp.Method = System.Net.WebRequestMethods.Ftp.DeleteFile;
  329:             try
  330:             {
  331:                 //get response but ignore it
  332:                 string str = GetStringResponse(ftp);
  333:             }
  334:             catch (Exception)
  335:             {
  336:                 return false;
  337:             }
  338:             return true;
  339:         }
  340:  
  341:         ////////////////////////////////////////////////////////////////////////////
  342:  
  343:         /// <summary>
  344:         /// Determine if file exists on remote FTP site
  345:         /// </summary>
  346:         /// <param name="filename">Filename (for current dir) or full path</param>
  347:         /// <returns></returns>
  348:         /// <remarks> Note this only works for files</remarks>
  349:         public bool FtpFileExists(string filename)
  350:         {
  351:             //Try to obtain filesize: if we get error msg containing "550"
  352:             //the file does not exist
  353:             try
  354:             {
  355:                 long size = GetFileSize(filename);
  356:                 return true;
  357:  
  358:             }
  359:             catch (Exception ex)
  360:             {
  361:                 //only handle expected not-found exception
  362:                 if (ex is System.Net.WebException)
  363:                 {
  364:                     //file does not exist/no rights error = 550
  365:                     if (ex.Message.Contains("550"))
  366:                     {
  367:                         //clear
  368:                         return false;
  369:                     }
  370:                     else
  371:                     {
  372:                         throw;
  373:                     }
  374:                 }
  375:                 else
  376:                 {
  377:                     throw;
  378:                 }
  379:             }
  380:         }
  381:  
  382:         ////////////////////////////////////////////////////////////////////////////
  383:  
  384:         /// <summary>
  385:         /// Determine size of remote file
  386:         /// </summary>
  387:         /// <param name="filename"></param>
  388:         /// <returns></returns>
  389:         /// <remarks> Throws an exception if file does not exist</remarks>
  390:         public long GetFileSize(string filename)
  391:         {
  392:             string path;
  393:             if (filename.Contains("/"))
  394:             {
  395:                 path = AdjustDir(filename);
  396:             }
  397:             else
  398:             {
  399:                 path = this.CurrentDirectory + filename;
  400:             }
  401:             string URI = this.Hostname + path;
  402:             System.Net.FtpWebRequest ftp = GetRequest(URI);
  403:             //Try to get info on file/dir?
  404:             ftp.Method = System.Net.WebRequestMethods.Ftp.GetFileSize;
  405:             string tmp = this.GetStringResponse(ftp);
  406:             return GetSize(ftp);
  407:         }
  408:  
  409:         ////////////////////////////////////////////////////////////////////////////
  410:  
  411:         /// <summary>
  412:         ///
  413:         /// </summary>
  414:         public bool FtpRename(string sourceFilename, string newName)
  415:         {
  416:             //Does file exist?
  417:             string source = GetFullPath(sourceFilename);
  418:             if (!FtpFileExists(source))
  419:             {
  420:                 throw (new FileNotFoundException("File " + source + " not found"));
  421:             }
  422:  
  423:             //build target name, ensure it does not exist
  424:             string target = GetFullPath(newName);
  425:             if (target == source)
  426:             {
  427:                 throw (new ApplicationException("Source and target are the same"));
  428:             }
  429:             else if (FtpFileExists(target))
  430:             {
  431:                 throw (new ApplicationException("Target file " + target + " already exists"));
  432:             }
  433:  
  434:             //perform rename
  435:             string URI = this.Hostname + source;
  436:  
  437:             System.Net.FtpWebRequest ftp = GetRequest(URI);
  438:             //Set request to delete
  439:             ftp.Method = System.Net.WebRequestMethods.Ftp.Rename;
  440:             ftp.RenameTo = target;
  441:             try
  442:             {
  443:                 //get response but ignore it
  444:                 string str = GetStringResponse(ftp);
  445:             }
  446:             catch (Exception)
  447:             {
  448:                 return false;
  449:             }
  450:             return true;
  451:         }
  452:  
  453:         ////////////////////////////////////////////////////////////////////////////
  454:  
  455:         /// <summary>
  456:         ///
  457:         /// </summary>
  458:         public bool FtpCreateDirectory(string dirpath)
  459:         {
  460:             //perform create
  461:             string URI = this.Hostname + AdjustDir(dirpath);
  462:             System.Net.FtpWebRequest ftp = GetRequest(URI);
  463:             //Set request to MkDir
  464:             ftp.Method = System.Net.WebRequestMethods.Ftp.MakeDirectory;
  465:             try
  466:             {
  467:                 //get response but ignore it
  468:                 string str = GetStringResponse(ftp);
  469:             }
  470:             catch (Exception)
  471:             {
  472:                 return false;
  473:             }
  474:             return true;
  475:         }
  476:  
  477:         ////////////////////////////////////////////////////////////////////////////
  478:  
  479:         /// <summary>
  480:         ///
  481:         /// </summary>
  482:         public bool FtpDeleteDirectory(string dirpath)
  483:         {
  484:             //perform remove
  485:             string URI = this.Hostname + AdjustDir(dirpath);
  486:             System.Net.FtpWebRequest ftp = GetRequest(URI);
  487:             //Set request to RmDir
  488:             ftp.Method = System.Net.WebRequestMethods.Ftp.RemoveDirectory;
  489:             try
  490:             {
  491:                 //get response but ignore it
  492:                 string str = GetStringResponse(ftp);
  493:             }
  494:             catch (Exception)
  495:             {
  496:                 return false;
  497:             }
  498:             return true;
  499:         }
  500:  
  501:         //Get the basic FtpWebRequest object with the
  502:         //common settings and security
  503:  
  504:         ////////////////////////////////////////////////////////////////////////////
  505:  
  506:         /// <summary>
  507:         ///
  508:         /// </summary>
  509:         private FtpWebRequest GetRequest(string URI)
  510:         {
  511:             //create request
  512:             FtpWebRequest result = (FtpWebRequest)FtpWebRequest.Create(URI);
  513:             //Set the login details
  514:             result.Credentials = GetCredentials();
  515:             //Do not keep alive (stateless mode)
  516:             result.KeepAlive = false;
  517:             return result;
  518:         }
  519:  
  520:         ////////////////////////////////////////////////////////////////////////////
  521:  
  522:         /// <summary>
  523:         /// Get the credentials from username/password
  524:         /// </summary>
  525:         private System.Net.ICredentials GetCredentials()
  526:         {
  527:             return new System.Net.NetworkCredential(Username, Password);
  528:         }
  529:  
  530:         ////////////////////////////////////////////////////////////////////////////
  531:  
  532:         /// <summary>
  533:         /// returns a full path using CurrentDirectory for a relative file reference
  534:         /// </summary>
  535:         private string GetFullPath(string file)
  536:         {
  537:             if (file.Contains("/"))
  538:             {
  539:                 return AdjustDir(file);
  540:             }
  541:             else
  542:             {
  543:                 return this.CurrentDirectory + file;
  544:             }
  545:         }
  546:  
  547:         ////////////////////////////////////////////////////////////////////////////
  548:  
  549:         /// <summary>
  550:         /// Amend an FTP path so that it always starts with /
  551:         /// </summary>
  552:         /// <param name="path">Path to adjust</param>
  553:         /// <returns></returns>
  554:         /// <remarks> </remarks>
  555:         private string AdjustDir(string path)
  556:         {
  557:             return ((path.StartsWith("/")) ? "" : "/").ToString() + path;
  558:         }
  559:  
  560:         ////////////////////////////////////////////////////////////////////////////
  561:  
  562:         /// <summary>
  563:         ///
  564:         /// </summary>
  565:         private string GetDirectory(string directory)
  566:         {
  567:             string URI;
  568:             if (directory == "")
  569:             {
  570:                 //build from current
  571:                 URI = Hostname + this.CurrentDirectory;
  572:                 _lastDirectory = this.CurrentDirectory;
  573:             }
  574:             else
  575:             {
  576:                 if (!directory.StartsWith("/"))
  577:                 {
  578:                     throw (new ApplicationException("Directory should start with /"));
  579:                 }
  580:                 URI = this.Hostname + directory;
  581:                 _lastDirectory = directory;
  582:             }
  583:             return URI;
  584:         }
  585:  
  586:         //stores last retrieved/set directory
  587:         private string _lastDirectory = "";
  588:  
  589:         ////////////////////////////////////////////////////////////////////////////
  590:  
  591:         /// <summary>
  592:         /// Obtains a response stream as a string
  593:         /// </summary>
  594:         /// <param name="ftp">current FTP request</param>
  595:         /// <returns>String containing response</returns>
  596:         /// <remarks> FTP servers typically return strings with CR and
  597:         /// not CRLF. Use respons.Replace(vbCR, vbCRLF) to convert
  598:         /// to an MSDOS string</remarks>
  599:         private string GetStringResponse(FtpWebRequest ftp)
  600:         {
  601:             //Get the result, streaming to a string
  602:             string result = "";
  603:             using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
  604:             {
  605:                 long size = response.ContentLength;
  606:                 using (Stream datastream = response.GetResponseStream())
  607:                 {
  608:                     using (StreamReader sr = new StreamReader(datastream))
  609:                     {
  610:                         result = sr.ReadToEnd();
  611:                         //sr.Close();
  612:                     }
  613:  
  614:                     //datastream.Close();
  615:                 }
  616:  
  617:                 //response.Close();
  618:             }
  619:  
  620:             return result;
  621:         }
  622:  
  623:         ////////////////////////////////////////////////////////////////////////////
  624:  
  625:         /// <summary>
  626:         /// Gets the size of an FTP request
  627:         /// </summary>
  628:         /// <param name="ftp"></param>
  629:         /// <returns></returns>
  630:         /// <remarks> </remarks>
  631:         private long GetSize(FtpWebRequest ftp)
  632:         {
  633:             long size;
  634:             using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
  635:             {
  636:                 size = response.ContentLength;
  637:  
  638:                 //response.Close();
  639:             }
  640:  
  641:             return size;
  642:         }
  643:  
  644:         private string _hostname;
  645:  
  646:         ////////////////////////////////////////////////////////////////////////////
  647:  
  648:         /// <summary>
  649:         /// Hostname
  650:         /// </summary>
  651:         /// <value></value>
  652:         /// <remarks> Hostname can be in either the full URL format
  653:         /// ftp://ftp.myhost.com or just ftp.myhost.com
  654:         /// </remarks>
  655:         public string Hostname
  656:         {
  657:             get
  658:             {
  659:                 if (_hostname.StartsWith("ftp://"))
  660:                 {
  661:                     return _hostname;
  662:                 }
  663:                 else
  664:                 {
  665:                     return "ftp://" + _hostname;
  666:                 }
  667:             }
  668:             set
  669:             {
  670:                 _hostname = value;
  671:             }
  672:         }
  673:         private string _username;
  674:  
  675:         ////////////////////////////////////////////////////////////////////////////
  676:  
  677:         /// <summary>
  678:         /// Username property
  679:         /// </summary>
  680:         /// <value></value>
  681:         /// <remarks> Can be left blank, in which case 'anonymous' is returned</remarks>
  682:         public string Username
  683:         {
  684:             get
  685:             {
  686:                 return (_username == "" ? "anonymous" : _username);
  687:             }
  688:             set
  689:             {
  690:                 _username = value;
  691:             }
  692:         }
  693:         private string _password;
  694:  
  695:         ////////////////////////////////////////////////////////////////////////////
  696:  
  697:         /// <summary>
  698:         ///
  699:         /// </summary>
  700:         public string Password
  701:         {
  702:             get
  703:             {
  704:                 return _password;
  705:             }
  706:             set
  707:             {
  708:                 _password = value;
  709:             }
  710:         }
  711:  
  712:         ////////////////////////////////////////////////////////////////////////////
  713:  
  714:         /// <summary>
  715:         /// The CurrentDirectory value
  716:         /// </summary>
  717:         /// <remarks> Defaults to the root '/'</remarks>
  718:         private string _currentDirectory = "/";
  719:  
  720:         ////////////////////////////////////////////////////////////////////////////
  721:  
  722:         /// <summary>
  723:         ///
  724:         /// </summary>
  725:         public string CurrentDirectory
  726:         {
  727:             get
  728:             {
  729:                 //return directory, ensure it ends with /
  730:                 return _currentDirectory + ((_currentDirectory.EndsWith("/")) ? "" : "/").ToString();
  731:             }
  732:             set
  733:             {
  734:                 if (!value.StartsWith("/"))
  735:                 {
  736:                     throw (new ApplicationException("Directory should start with /"));
  737:                 }
  738:                 _currentDirectory = value;
  739:             }
  740:         }
  741:     }
  742:  
  743:     ////////////////////////////////////////////////////////////////////////////
  744:     ////////////////////////////////////////////////////////////////////////////
  745:  
  746:     /// <summary>
  747:     /// Represents a file or directory entry from an FTP listing
  748:     /// </summary>
  749:     /// <remarks> 
  750:     /// This class is used to parse the results from a detailed
  751:     /// directory list from FTP. It supports most formats of
  752:     /// </remarks>
  753:     public class FtpFileInfo
  754:     {
  755:  
  756:         //Stores extended info about FTP file
  757:  
  758:         /// <summary/>
  759:         public string FullName
  760:         {
  761:             get
  762:             {
  763:                 return Path + Filename;
  764:             }
  765:         }
  766:  
  767:         /// <summary/>
  768:         public string Filename
  769:         {
  770:             get
  771:             {
  772:                 return _filename;
  773:             }
  774:         }
  775:  
  776:         /// <summary/>
  777:         public string Path
  778:         {
  779:             get
  780:             {
  781:                 return _path;
  782:             }
  783:         }
  784:  
  785:         /// <summary/>
  786:         public DirectoryEntryTypes FileType
  787:         {
  788:             get
  789:             {
  790:                 return _fileType;
  791:             }
  792:         }
  793:  
  794:         /// <summary/>
  795:         public long Size
  796:         {
  797:             get
  798:             {
  799:                 return _size;
  800:             }
  801:         }
  802:  
  803:         /// <summary/>
  804:         public DateTime FileDateTime
  805:         {
  806:             get
  807:             {
  808:                 return _fileDateTime;
  809:             }
  810:         }
  811:  
  812:         /// <summary/>
  813:         public string Permission
  814:         {
  815:             get
  816:             {
  817:                 return _permission;
  818:             }
  819:         }
  820:  
  821:         /// <summary/>
  822:         public string Extension
  823:         {
  824:             get
  825:             {
  826:                 int i = this.Filename.LastIndexOf(".");
  827:                 if (i >= 0 && i < (this.Filename.Length - 1))
  828:                 {
  829:                     return this.Filename.Substring(i + 1);
  830:                 }
  831:                 else
  832:                 {
  833:                     return "";
  834:                 }
  835:             }
  836:         }
  837:  
  838:         /// <summary/>
  839:         public string NameOnly
  840:         {
  841:             get
  842:             {
  843:                 int i = this.Filename.LastIndexOf(".");
  844:                 if (i > 0)
  845:                 {
  846:                     return this.Filename.Substring(0, i);
  847:                 }
  848:                 else
  849:                 {
  850:                     return this.Filename;
  851:                 }
  852:             }
  853:         }
  854:  
  855:         private string _filename;
  856:         private string _path;
  857:         private DirectoryEntryTypes _fileType;
  858:         private long _size;
  859:         private DateTime _fileDateTime;
  860:         private string _permission;
  861:  
  862:         /// <summary>
  863:         /// Identifies entry as either File or Directory
  864:         /// </summary>
  865:         public enum DirectoryEntryTypes
  866:         {
  867:             /// <summary/>
  868:             File,
  869:  
  870:             /// <summary/>
  871:             Directory
  872:         }
  873:  
  874:         /// <summary>
  875:         /// Constructor taking a directory listing line and path
  876:         /// </summary>
  877:         /// <param name="line">The line returned from the detailed directory list</param>
  878:         /// <param name="path">Path of the directory</param>
  879:         /// <remarks> </remarks>
  880:         public FtpFileInfo(string line, string path)
  881:         {
  882:             //parse line
  883:             Match m = GetMatchingRegex(line);
  884:             if (m == null)
  885:             {
  886:                 //failed
  887:                 throw (new ApplicationException("Unable to parse line: " + line));
  888:             }
  889:             else
  890:             {
  891:                 _filename = m.Groups["name"].Value;
  892:                 _path = path;
  893:  
  894:                 Int64.TryParse(m.Groups["size"].Value, out _size);
  895:                 //_size = System.Convert.ToInt32(m.Groups["size"].Value);
  896:  
  897:                 _permission = m.Groups["permission"].Value;
  898:                 string _dir = m.Groups["dir"].Value;
  899:                 if (_dir != "" && _dir != "-")
  900:                 {
  901:                     _fileType = DirectoryEntryTypes.Directory;
  902:                 }
  903:                 else
  904:                 {
  905:                     _fileType = DirectoryEntryTypes.File;
  906:                 }
  907:  
  908:                 try
  909:                 {
  910:                     _fileDateTime = DateTime.Parse(m.Groups["timestamp"].Value);
  911:                 }
  912:                 catch (Exception)
  913:                 {
  914:                     _fileDateTime = Convert.ToDateTime(null);
  915:                 }
  916:  
  917:             }
  918:         }
  919:  
  920:         ////////////////////////////////////////////////////////////////////////////
  921:  
  922:         /// <summary>
  923:         ///
  924:         /// </summary>
  925:         private Match GetMatchingRegex(string line)
  926:         {
  927:             Regex rx;
  928:             Match m;
  929:             for (int i = 0; i <= _ParseFormats.Length - 1; i++)
  930:             {
  931:                 rx = new Regex(_ParseFormats[i]);
  932:                 m = rx.Match(line);
  933:                 if (m.Success)
  934:                 {
  935:                     return m;
  936:                 }
  937:             }
  938:             return null;
  939:         }
  940:  
  941:         /// <summary>
  942:         /// List of REGEX formats for different FTP server listing formats
  943:         /// </summary>
  944:         /// <remarks> 
  945:         /// The first three are various UNIX/LINUX formats, fourth is for MS FTP
  946:         /// in detailed mode and the last for MS FTP in 'DOS' mode.
  947:         /// I wish VB.NET had support for Const arrays like C# but there you go
  948:         /// </remarks>
  949:         private static string[] _ParseFormats = new string[] {
  950:             "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\w+\\s+\\w+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{4})\\s+(?<name>.+)",
  951:             "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\d+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{4})\\s+(?<name>.+)",
  952:             "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\d+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{1,2}:\\d{2})\\s+(?<name>.+)",
  953:             "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\w+\\s+\\w+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{1,2}:\\d{2})\\s+(?<name>.+)",
  954:             "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})(\\s+)(?<size>(\\d+))(\\s+)(?<ctbit>(\\w+\\s\\w+))(\\s+)(?<size2>(\\d+))\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{2}:\\d{2})\\s+(?<name>.+)",
  955:             "(?<timestamp>\\d{2}\\-\\d{2}\\-\\d{2}\\s+\\d{2}:\\d{2}[Aa|Pp][mM])\\s+(?<dir>\\<\\w+\\>){0,1}(?<size>\\d+){0,1}\\s+(?<name>.+)" };
  956:     }
  957:  
  958:     /// <summary>
  959:     /// Stores a list of files and directories from an FTP result
  960:     /// </summary>
  961:     /// <remarks> </remarks>
  962:     public class FtpDirectory : List<FtpFileInfo>
  963:     {
  964:         ////////////////////////////////////////////////////////////////////////////
  965:  
  966:         /// <summary>
  967:         ///
  968:         /// </summary>
  969:         public FtpDirectory()
  970:         {
  971:             //creates a blank directory listing
  972:         }
  973:  
  974:         /// <summary>
  975:         /// Constructor: create list from a (detailed) directory string
  976:         /// </summary>
  977:         /// <param name="dir">directory listing string</param>
  978:         /// <param name="path"></param>
  979:         /// <remarks> </remarks>
  980:         public FtpDirectory(string dir, string path)
  981:         {
  982:             foreach (string line in dir.Replace("\n", "").Split(System.Convert.ToChar('\r')))
  983:             {
  984:                 //parse
  985:                 if (line != "")
  986:                 {
  987:                     this.Add(new FtpFileInfo(line, path));
  988:                 }
  989:             }
  990:         }
  991:  
  992:         /// <summary>
  993:         /// Filter out only files from directory listing
  994:         /// </summary>
  995:         /// <param name="ext">optional file extension filter</param>
  996:         /// <returns>FtpDirectory listing</returns>
  997:         public FtpDirectory GetFiles(string ext)
  998:         {
  999:             return this.GetFileOrDir(FtpFileInfo.DirectoryEntryTypes.File, ext);
 1000:         }
 1001:  
 1002:         /// <summary>
 1003:         /// Returns a list of only subdirectories
 1004:         /// </summary>
 1005:         /// <returns>FtpDirectory list</returns>
 1006:         /// <remarks> </remarks>
 1007:         public FtpDirectory GetDirectories()
 1008:         {
 1009:             return this.GetFileOrDir(FtpFileInfo.DirectoryEntryTypes.Directory, "");
 1010:         }
 1011:  
 1012:         //internal: share use function for GetDirectories/Files
 1013:         private FtpDirectory GetFileOrDir(FtpFileInfo.DirectoryEntryTypes type, string ext)
 1014:         {
 1015:             FtpDirectory result = new FtpDirectory();
 1016:             foreach (FtpFileInfo fi in this)
 1017:             {
 1018:                 if (fi.FileType == type)
 1019:                 {
 1020:                     if (ext == "")
 1021:                     {
 1022:                         result.Add(fi);
 1023:                     }
 1024:                     else if (ext == fi.Extension)
 1025:                     {
 1026:                         result.Add(fi);
 1027:                     }
 1028:                 }
 1029:             }
 1030:             return result;
 1031:  
 1032:         }
 1033:  
 1034:         /// <summary/>
 1035:         public bool FileExists(string filename)
 1036:         {
 1037:             foreach (FtpFileInfo ftpfile in this)
 1038:             {
 1039:                 if (ftpfile.Filename == filename)
 1040:                 {
 1041:                     return true;
 1042:                 }
 1043:             }
 1044:             return false;
 1045:         }
 1046:  
 1047:         private const char slash = '/';
 1048:  
 1049:         /// <summary/>
 1050:         public static string GetParentDirectory(string dir)
 1051:         {
 1052:             string tmp = dir.TrimEnd(slash);
 1053:             int i = tmp.LastIndexOf(slash);
 1054:             if (i > 0)
 1055:             {
 1056:                 return tmp.Substring(0, i - 1);
 1057:             }
 1058:             else
 1059:             {
 1060:                 throw (new ApplicationException("No parent for root"));
 1061:             }
 1062:         }
 1063:     }
 1064: }
 1065: