شركة التطبيقات المتكاملة لتصميم النظم البرمجية الخاصة ش.ش.و.

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);
        }
 
        /// <summary>
        /// Returns a list of only subdirectories
        /// </summary>
        /// <returns>FtpDirectory list</returns>
        /// <remarks> </remarks>
        public FtpDirectory GetDirectories()
        {
            return this.GetFileOrDir(FtpFileInfo.DirectoryEntryTypes.Directory, "");
        }
 
        //internal: share use function for GetDirectories/Files
        private FtpDirectory GetFileOrDir(FtpFileInfo.DirectoryEntryTypes type, string ext)
        {
            FtpDirectory result = new FtpDirectory();
            foreach (FtpFileInfo fi in this)
            {
                if (fi.FileType == type)
                {
                    if (ext == "")
                    {
                        result.Add(fi);
                    }
                    else if (ext == fi.Extension)
                    {
                        result.Add(fi);
                    }
                }
            }
            return result;
 
        }
 
        /// <summary/>
        public bool FileExists(string filename)
        {
            foreach (FtpFileInfo ftpfile in this)
            {
                if (ftpfile.Filename == filename)
                {
                    return true;
                }
            }
            return false;
        }
 
        private const char slash = '/';
 
        /// <summary/>
        public static string GetParentDirectory(string dir)
        {
            string tmp = dir.TrimEnd(slash);
            int i = tmp.LastIndexOf(slash);
            if (i > 0)
            {
                return tmp.Substring(0, i - 1);
            }
            else
            {
                throw (new ApplicationException("No parent for root"));
            }
        }
    }
}