file360

Log | Files | Refs

FtpClient.cs (22757B)


      1 using System;
      2 using System.Collections.Generic;
      3 using System.IO;
      4 using System.Linq;
      5 using System.Runtime.InteropServices.WindowsRuntime;
      6 using System.Threading.Tasks;
      7 using WinPhoneFtp.FtpService;
      8 using Windows.Networking.Sockets;
      9 using Windows.Storage.Streams;
     10 using System.Windows.Threading;
     11 
     12 namespace WinPhoneFtp.FtpService
     13 {
     14     public class FtpClient
     15     {
     16         String Username = String.Empty;
     17         String Password = String.Empty;
     18         TcpClientSocket FtpCommandSocket = null;
     19         FtpCommand ftpCommand = FtpCommand.None;
     20         FtpPassiveOperation ftpPassiveOperation = FtpPassiveOperation.None;
     21         FtpFileOperationInfo ftpFileInfo = null;
     22         StreamSocket FtpDataChannel = null;
     23         List<byte> fileListingData = null;
     24         String RemoteDirectory = String.Empty;
     25         Logger logger = null;
     26         Dispatcher UIDispatcher = null;
     27 
     28         public event EventHandler FtpConnected;
     29         public event EventHandler FtpAuthenticationSucceeded;
     30         public event EventHandler FtpAuthenticationFailed;
     31         public event EventHandler<FtpDirectoryChangedEventArgs> FtpDirectoryChangedSucceded;
     32         public event EventHandler<FtpDirectoryChangedEventArgs> FtpDirectoryChangedFailed;
     33         public event EventHandler<FtpDisconnectedEventArgs> FtpDisconnected;
     34         public event EventHandler<FtpPresentWorkingDirectoryEventArgs> FtpPresentWorkingDirectoryReceived;
     35         public event EventHandler<FtpFileTransferEventArgs> FtpFileUploadSucceeded;
     36         public event EventHandler<FtpFileTransferFailedEventArgs> FtpFileUploadFailed;
     37         public event EventHandler<FtpFileTransferEventArgs> FtpFileDownloadSucceeded;
     38         public event EventHandler<FtpFileTransferFailedEventArgs> FtpFileDownloadFailed;
     39         public event EventHandler<FtpDirectoryListedEventArgs> FtpDirectoryListed;
     40         public event EventHandler<FtpFileTransferProgressedEventArgs> FtpFileTransferProgressed;
     41 
     42         public FtpClient(String FtpServerIpAddress, Dispatcher UIDispatcher)
     43             : this(FtpServerIpAddress, "21", UIDispatcher)
     44         {
     45         }
     46 
     47         public FtpClient(String FtpServerIpAddress, String PortNumber, Dispatcher UIDispatcher)
     48         {
     49             this.UIDispatcher = UIDispatcher;
     50             logger = Logger.GetDefault(UIDispatcher);
     51             logger.AddLog(String.Format("FTP Server IP Address: {0} with port {1}", FtpServerIpAddress, PortNumber));
     52             FtpCommandSocket = new TcpClientSocket(FtpServerIpAddress, PortNumber, 512, "FTP Command Channel", UIDispatcher);
     53             FtpCommandSocket.DataReceived += FtpClientSocket_DataReceived;
     54             FtpCommandSocket.ErrorOccured += FtpClientSocket_ErrorOccured;
     55             FtpCommandSocket.SocketClosed += FtpClientSocket_SocketClosed;
     56             FtpCommandSocket.SocketConnected += FtpCommandSocket_SocketConnected;
     57             IsConnected = false;
     58         }
     59 
     60         public Boolean IsConnected
     61         {
     62             get;
     63             private set;
     64         }
     65 
     66         public Boolean IsBusy
     67         {
     68             get;
     69             private set;
     70         }
     71 
     72         public async Task ConnectAsync()
     73         {
     74             if (!IsConnected)
     75             {
     76                 logger.AddLog("FTP Command Channel Initailized");
     77                 await FtpCommandSocket.PrepareSocketAsync();
     78             }
     79         }
     80 
     81         async void FtpClientSocket_DataReceived(object sender, DataReceivedEventArgs e)
     82         {
     83             String Response = System.Text.Encoding.UTF8.GetString(e.GetData(), 0, e.GetData().Length);
     84             logger.AddLog(String.Format("FTPServer -> {0}", Response));
     85             switch (ftpPassiveOperation)
     86             {
     87                 case FtpPassiveOperation.FileDownload:
     88                     ftpCommand = FtpCommand.None;
     89                     if (Response.StartsWith("150") || Response.StartsWith("125"))
     90                     {
     91                         IsBusy = true;
     92                         DataReader dataReader = new DataReader(FtpDataChannel.InputStream);
     93                         dataReader.InputStreamOptions = InputStreamOptions.Partial;
     94                         while (!(await dataReader.LoadAsync(32768)).Equals(0))
     95                         {
     96                             IBuffer databuffer = dataReader.DetachBuffer();
     97                             RaiseFtpFileTransferProgressedEvent(databuffer.Length, false);
     98                             await ftpFileInfo.LocalFileStream.WriteAsync(databuffer.ToArray(), 0, Convert.ToInt32(databuffer.Length));
     99                         }
    100                         await ftpFileInfo.LocalFileStream.FlushAsync();
    101                         dataReader.Dispose();
    102                         dataReader = null;
    103                         //FtpDataChannel.Dispose();
    104                         //FtpDataChannel = null;
    105                         RaiseFtpFileDownloadSucceededEvent(ftpFileInfo.LocalFileStream, ftpFileInfo.RemoteFile);
    106                     }
    107                     else if (Response.StartsWith("226"))
    108                     {
    109                         IsBusy = false;
    110                         ftpPassiveOperation = FtpPassiveOperation.None;
    111                     }
    112                     else
    113                     {
    114                         IsBusy = false;
    115                         ftpPassiveOperation = FtpPassiveOperation.None;
    116                         RaiseFtpFileDownloadFailedEvent(ftpFileInfo.LocalFileStream, ftpFileInfo.RemoteFile, FtpFileTransferFailureReason.FileDoesNotExist);
    117                     }
    118                     break;
    119 
    120                 case FtpPassiveOperation.FileUpload:
    121                     ftpCommand = FtpCommand.None;
    122                     if (Response.StartsWith("150") || Response.StartsWith("125"))
    123                     {
    124                         IsBusy = true;
    125                         DataWriter dataWriter = new DataWriter(FtpDataChannel.OutputStream);
    126                         byte[] data = new byte[32768];
    127                         while (!(await ftpFileInfo.LocalFileStream.ReadAsync(data, 0, data.Length)).Equals(0))
    128                         {
    129                             dataWriter.WriteBytes(data);
    130                             await dataWriter.StoreAsync();
    131                             RaiseFtpFileTransferProgressedEvent(Convert.ToUInt32(data.Length), true);
    132                         }
    133                         await dataWriter.FlushAsync();
    134                         dataWriter.Dispose();
    135                         dataWriter = null;
    136                         FtpDataChannel.Dispose();
    137                         FtpDataChannel = null;
    138                     }
    139                     else if (Response.StartsWith("226"))
    140                     {
    141                         IsBusy = false;
    142                         ftpPassiveOperation = FtpPassiveOperation.None;
    143                         RaiseFtpFileUploadSucceededEvent(ftpFileInfo.LocalFileStream, ftpFileInfo.RemoteFile);
    144                         ftpFileInfo = null;
    145                     }
    146                     else
    147                     {
    148                         IsBusy = false;
    149                         ftpPassiveOperation = FtpPassiveOperation.None;
    150                         RaiseFtpFileUploadFailedEvent(ftpFileInfo.LocalFileStream, ftpFileInfo.RemoteFile, FtpFileTransferFailureReason.FileDoesNotExist);
    151                         ftpFileInfo = null;
    152                     }
    153                     break;
    154 
    155                 case FtpPassiveOperation.ListDirectory:
    156                     ftpCommand = FtpCommand.None;
    157                     if (Response.StartsWith("150") || Response.StartsWith("125"))
    158                     {
    159                         IsBusy = true;
    160                         DataReader dataReader = new DataReader(FtpDataChannel.InputStream);
    161                         dataReader.InputStreamOptions = InputStreamOptions.Partial;
    162                         fileListingData = new List<byte>();
    163                         while (!(await dataReader.LoadAsync(1024)).Equals(0))
    164                         {
    165                             fileListingData.AddRange(dataReader.DetachBuffer().ToArray());
    166                         }
    167                         dataReader.Dispose();
    168                         dataReader = null;
    169                         FtpDataChannel.Dispose();
    170                         FtpDataChannel = null;
    171                         String listingData = System.Text.Encoding.UTF8.GetString(fileListingData.ToArray(), 0, fileListingData.ToArray().Length);
    172                         String[] listings = listingData.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
    173                         List<String> Filenames = new List<String>();
    174                         List<String> Directories = new List<String>();
    175                         foreach (String listing in listings)
    176                         {
    177                             if (listing.StartsWith("drwx") || listing.Contains("<DIR>"))
    178                             {
    179                                 Directories.Add(listing.Split(new char[] { ' ' }).Last());
    180                             }
    181                             else
    182                             {
    183                                 Filenames.Add(listing.Split(new char[] { ' ' }).Last());
    184                             }
    185                         }
    186                         RaiseFtpDirectoryListedEvent(Directories.ToArray(), Filenames.ToArray());
    187                     }
    188                     else if (Response.StartsWith("226"))
    189                     {
    190                         IsBusy = false;
    191                         ftpFileInfo = null;
    192                         ftpPassiveOperation = FtpPassiveOperation.None;
    193                     }
    194                     break;
    195 
    196                 case FtpPassiveOperation.None:
    197                     switch (ftpCommand)
    198                     {
    199                         case FtpCommand.Username:
    200                             if (Response.StartsWith("501"))
    201                             {
    202                                 IsBusy = false;
    203                                 RaiseFtpAuthenticationFailedEvent();
    204                                 break;
    205                             }
    206                             this.ftpCommand = FtpCommand.Password;
    207                             logger.AddLog(String.Format("FTPClient -> PASS {0}\r\n", this.Password));
    208                             await FtpCommandSocket.SendDataAsync(String.Format("PASS {0}\r\n", this.Password));
    209                             break;
    210 
    211                         case FtpCommand.Password:
    212                             this.ftpCommand = FtpCommand.None;
    213                             IsBusy = false;
    214                             if (Response.Contains("530"))
    215                             {
    216                                 RaiseFtpAuthenticationFailedEvent();
    217                             }
    218                             else
    219                             {
    220                                 RaiseFtpAuthenticationSucceededEvent();
    221                             }
    222                             break;
    223 
    224                         case FtpCommand.ChangeWorkingDirectory:
    225                             IsBusy = false;
    226                             if (Response.StartsWith("550"))
    227                             {
    228                                 RaiseFtpDirectoryChangedFailedEvent(this.RemoteDirectory);
    229                             }
    230                             else
    231                             {
    232                                 RaiseFtpDirectoryChangedSuccededEvent(this.RemoteDirectory);
    233                             }
    234                             break;
    235 
    236                         case FtpCommand.PresentWorkingDirectory:
    237                             if (Response.StartsWith("257"))
    238                             {
    239                                 IsBusy = false;
    240                                 RaiseFtpPresentWorkingDirectoryReceivedEvent(Response.Split(new Char[] { ' ', '"' }, StringSplitOptions.RemoveEmptyEntries)[1]);
    241                             }
    242                             break;
    243 
    244                         case FtpCommand.Type:
    245                             ftpCommand = FtpCommand.Passive;
    246                             logger.AddLog("FTPClient -> PASV\r\n");
    247                             await FtpCommandSocket.SendDataAsync("PASV\r\n");
    248                             break;
    249 
    250                         case FtpCommand.Passive:
    251                             if (Response.StartsWith("227"))
    252                             {
    253                                 await PrepareDataChannelAsync(Response);
    254                                 if (ftpFileInfo != null)
    255                                 {
    256                                     if (ftpFileInfo.IsUpload)
    257                                     {
    258                                         ftpPassiveOperation = FtpPassiveOperation.FileUpload;
    259                                         logger.AddLog(String.Format("FTPClient -> STOR {0}\r\n", ftpFileInfo.RemoteFile));
    260                                         await FtpCommandSocket.SendDataAsync(String.Format("STOR {0}\r\n", ftpFileInfo.RemoteFile));
    261                                     }
    262                                     else
    263                                     {
    264                                         ftpPassiveOperation = FtpPassiveOperation.FileDownload;
    265                                         logger.AddLog(String.Format("FTPClient -> RETR {0}\r\n", ftpFileInfo.RemoteFile));
    266                                         await FtpCommandSocket.SendDataAsync(String.Format("RETR {0}\r\n", ftpFileInfo.RemoteFile));
    267                                     }
    268                                 }
    269                                 else
    270                                 {
    271                                     fileListingData = new List<byte>();
    272                                     ftpPassiveOperation = FtpPassiveOperation.ListDirectory;
    273                                     logger.AddLog("FTPClient -> LIST\r\n");
    274                                     await FtpCommandSocket.SendDataAsync("LIST\r\n");
    275                                 }
    276                             }
    277                             break;
    278 
    279                         case FtpCommand.Logout:
    280                             ftpCommand = FtpCommand.None;
    281                             break;
    282 
    283                         case FtpCommand.None:
    284                             break;
    285                     }
    286                     break;
    287             }
    288 
    289         }
    290 
    291         void FtpCommandSocket_SocketConnected(object sender, EventArgs e)
    292         {
    293             IsConnected = true;
    294             RaiseFtpConnectedEvent();
    295         }
    296 
    297         void FtpClientSocket_ErrorOccured(object sender, ErrorOccuredEventArgs e)
    298         {
    299             logger.AddLog(e.ExceptionObject.Message);
    300             IsConnected = false;
    301             RaiseFtpDisconnectedEvent(FtpDisconnectReason.SocketError);
    302         }
    303 
    304         void FtpClientSocket_SocketClosed(object sender, SocketClosedEventArgs e)
    305         {
    306             IsConnected = false;
    307             if (!ftpCommand.Equals(FtpCommand.Logout))
    308             {
    309                 RaiseFtpDisconnectedEvent(FtpDisconnectReason.SocketClosed);
    310             }
    311             else
    312             {
    313                 RaiseFtpDisconnectedEvent(FtpDisconnectReason.QuitCommand);
    314             }
    315         }
    316 
    317         //public async Task AuthenticateAsync()
    318         //{
    319         //    await AuthenticateAsync("anonymous", "m@m.com");	//TODO: test value only
    320         //}
    321 
    322         public async Task AuthenticateAsync(String Username, String Password)
    323         {
    324             ftpCommand = FtpCommand.Username;
    325             this.Username = Username;
    326             this.Password = Password;
    327             logger.AddLog(String.Format("FTPClient -> USER {0}\r\n", Username));
    328             await FtpCommandSocket.SendDataAsync(String.Format("USER {0}\r\n", Username));
    329         }
    330 
    331         public async Task ChangeWorkingDirectoryAsync(String RemoteDirectory)
    332         {
    333             if (!IsBusy)
    334             {
    335                 this.RemoteDirectory = RemoteDirectory;
    336                 ftpCommand = FtpCommand.ChangeWorkingDirectory;
    337                 logger.AddLog(String.Format("FTPClient -> CWD {0}\r\n", RemoteDirectory));
    338                 await FtpCommandSocket.SendDataAsync(String.Format("CWD {0}\r\n", RemoteDirectory));
    339             }
    340         }
    341 
    342         public async Task GetPresentWorkingDirectoryAsync()
    343         {
    344             if (!IsBusy)
    345             {
    346                 ftpCommand = FtpCommand.PresentWorkingDirectory;
    347                 logger.AddLog("FTPClient -> PWD\r\n");
    348                 await FtpCommandSocket.SendDataAsync("PWD\r\n");
    349             }
    350         }
    351 
    352         public async Task GetDirectoryListingAsync()
    353         {
    354             if (!IsBusy)
    355             {
    356                 fileListingData = null;
    357                 ftpFileInfo = null;
    358                 IsBusy = true;
    359                 ftpCommand = FtpCommand.Passive;
    360                 logger.AddLog("FTPClient -> PASV\r\n");
    361                 await FtpCommandSocket.SendDataAsync("PASV\r\n");
    362             }
    363         }
    364 
    365         public async Task UploadFileAsync(System.IO.Stream LocalFileStream, String RemoteFilename)
    366         {
    367             if (!IsBusy)
    368             {
    369                 ftpFileInfo = null;
    370                 IsBusy = true;
    371                 ftpFileInfo = new FtpFileOperationInfo(LocalFileStream, RemoteFilename, true);
    372                 ftpCommand = FtpCommand.Type;
    373                 logger.AddLog("FTPClient -> TYPE I\r\n");
    374                 await FtpCommandSocket.SendDataAsync("TYPE I\r\n");
    375             }
    376         }
    377 
    378         public async Task DownloadFileAsync(System.IO.Stream LocalFileStream, String RemoteFilename)
    379         {
    380             if (!IsBusy)
    381             {
    382                 ftpFileInfo = null;
    383                 IsBusy = true;
    384                 ftpFileInfo = new FtpFileOperationInfo(LocalFileStream, RemoteFilename, false);
    385                 ftpCommand = FtpCommand.Type;
    386                 logger.AddLog("FTPClient -> TYPE I\r\n");
    387                 await FtpCommandSocket.SendDataAsync("TYPE I\r\n");
    388             }
    389         }
    390 
    391         public async Task DisconnectAsync()
    392         {
    393             ftpCommand = FtpCommand.Logout;
    394             logger.AddLog("FTPClient -> QUIT\r\n");
    395             await FtpCommandSocket.SendDataAsync("QUIT\r\n");
    396         }
    397 
    398         private async Task PrepareDataChannelAsync(String ChannelInfo)
    399         {
    400             ChannelInfo = ChannelInfo.Remove(0, "227 Entering Passive Mode".Length);		//TODO: test value only
    401             //Configure the IP Address
    402             String[] Splits = ChannelInfo.Substring(ChannelInfo.IndexOf("(") + 1, ChannelInfo.Length - ChannelInfo.IndexOf("(") - 5).Split(new char[] { ',', ' ', }, StringSplitOptions.RemoveEmptyEntries);
    403             String Ipaddr = String.Join(".", Splits, 0, 4);
    404 
    405             //Calculate the Data Port
    406             Int32 port = Convert.ToInt32(Splits[4]);
    407             port = ((port << 8) | Convert.ToInt32(Splits[5]));
    408             logger.AddLog(String.Format("FTP Data Channel IPAddress: {0}, Port: {1}", Ipaddr, port));
    409             FtpDataChannel = new StreamSocket();
    410             await FtpDataChannel.ConnectAsync(new Windows.Networking.HostName(Ipaddr), port.ToString());
    411             logger.AddLog("FTP Data Channel connected");
    412         }
    413 
    414         private void RaiseFtpAuthenticationSucceededEvent()
    415         {
    416             if (FtpAuthenticationSucceeded != null)
    417             {
    418                 FtpAuthenticationSucceeded(this, EventArgs.Empty);
    419             }
    420         }
    421 
    422         private void RaiseFtpAuthenticationFailedEvent()
    423         {
    424             if (FtpAuthenticationFailed != null)
    425             {
    426                 FtpAuthenticationFailed(this, EventArgs.Empty);
    427             }
    428         }
    429 
    430         private void RaiseFtpDirectoryChangedSuccededEvent(String RemoteDirectory)
    431         {
    432             if (FtpDirectoryChangedSucceded != null)
    433             {
    434                 FtpDirectoryChangedSucceded(this, new FtpDirectoryChangedEventArgs(RemoteDirectory));
    435             }
    436         }
    437 
    438         private void RaiseFtpDirectoryChangedFailedEvent(String RemoteDirectory)
    439         {
    440             if (FtpDirectoryChangedFailed != null)
    441             {
    442                 FtpDirectoryChangedFailed(this, new FtpDirectoryChangedEventArgs(RemoteDirectory));
    443             }
    444         }
    445 
    446         private void RaiseFtpPresentWorkingDirectoryReceivedEvent(String PresentWorkingDirectory)
    447         {
    448             if (FtpPresentWorkingDirectoryReceived != null)
    449             {
    450                 FtpPresentWorkingDirectoryReceived(this, new FtpPresentWorkingDirectoryEventArgs(PresentWorkingDirectory));
    451             }
    452         }
    453 
    454         private void RaiseFtpFileUploadSucceededEvent(Stream LocalFileStream, String RemoteFile)
    455         {
    456             if (FtpFileUploadSucceeded != null)
    457             {
    458                 FtpFileUploadSucceeded(this, new FtpFileTransferEventArgs(LocalFileStream, RemoteFile));
    459             }
    460         }
    461 
    462         private void RaiseFtpFileUploadFailedEvent(Stream LocalFileStream, String RemoteFile, FtpFileTransferFailureReason FileTransferFailReason)
    463         {
    464             if (FtpFileUploadFailed != null)
    465             {
    466                 FtpFileUploadFailed(this, new FtpFileTransferFailedEventArgs(LocalFileStream, RemoteFile, FileTransferFailReason));
    467             }
    468         }
    469 
    470         private void RaiseFtpFileDownloadSucceededEvent(Stream LocalFileStream, String RemoteFile)
    471         {
    472             if (FtpFileDownloadSucceeded != null)
    473             {
    474                 FtpFileDownloadSucceeded(this, new FtpFileTransferEventArgs(LocalFileStream, RemoteFile));
    475             }
    476         }
    477 
    478         private void RaiseFtpFileDownloadFailedEvent(Stream LocalFileStream, String RemoteFile, FtpFileTransferFailureReason FileTransferFailReason)
    479         {
    480             if (FtpFileDownloadFailed != null)
    481             {
    482                 FtpFileDownloadFailed(this, new FtpFileTransferFailedEventArgs(LocalFileStream, RemoteFile, FileTransferFailReason));
    483             }
    484         }
    485 
    486         private void RaiseFtpConnectedEvent()
    487         {
    488             if (FtpConnected != null)
    489             {
    490                 FtpConnected(this, EventArgs.Empty);
    491             }
    492         }
    493 
    494         private void RaiseFtpDisconnectedEvent(FtpDisconnectReason DisconnectReason)
    495         {
    496             if (FtpDisconnected != null)
    497             {
    498                 FtpDisconnected(this, new FtpDisconnectedEventArgs(DisconnectReason));
    499             }
    500         }
    501 
    502         private void RaiseFtpDirectoryListedEvent(String[] Directories, String[] Filenames)
    503         {
    504             if (FtpDirectoryListed != null)
    505             {
    506                 FtpDirectoryListed(this, new FtpDirectoryListedEventArgs(Directories, Filenames));
    507             }
    508         }
    509 
    510         private void RaiseFtpFileTransferProgressedEvent(UInt32 BytesTransfered, Boolean IsUpload)
    511         {
    512             if (FtpFileTransferProgressed != null)
    513             {
    514                 FtpFileTransferProgressed(this, new FtpFileTransferProgressedEventArgs(BytesTransfered, IsUpload));
    515             }
    516         }
    517     }
    518 }