- Main Page
- Namespaces
- Classes
- Files

- File List

# Ftp.cpp

    00001
    00002 //
    00003 // SFML - Simple and Fast Multimedia Library
    00004 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
    00005 //
    00006 // This software is provided 'as-is', without any express or implied warranty.
    00007 // In no event will the authors be held liable for any damages arising from the use of this software.
    00008 //
    00009 // Permission is granted to anyone to use this software for any purpose,
    00010 // including commercial applications, and to alter it and redistribute it freely,
    00011 // subject to the following restrictions:
    00012 //
    00013 // 1. The origin of this software must not be misrepresented;
    00014 //    you must not claim that you wrote the original software.
    00015 //    If you use this software in a product, an acknowledgment
    00016 //    in the product documentation would be appreciated but is not required.
    00017 //
    00018 // 2. Altered source versions must be plainly marked as such,
    00019 //    and must not be misrepresented as being the original software.
    00020 //
    00021 // 3. This notice may not be removed or altered from any source distribution.
    00022 //
    00024
    00026 // Headers
    00028 #include <SFML/Network/Ftp.hpp>
    00029 #include <SFML/Network/IPAddress.hpp>
    00030 #include <algorithm>
    00031 #include <fstream>
    00032 #include <iterator>
    00033 #include <sstream>
    00034
    00035
    00036 namespace sf
    00037 {
    00039 // Utility class for exchanging stuff with the server
    00040 // on the data channel
    00042 class Ftp::DataChannel : NonCopyable
    00043 {
    00044 public :
    00045
    00047     // Constructor
    00049     DataChannel(Ftp& Owner);
    00050
    00052     // Destructor
    00054     ~DataChannel();
    00055
    00057     // Open the data channel using the specified mode and port
    00059     Ftp::Response Open(Ftp::TransferMode Mode);
    00060
    00062     // Send data on the data channel
    00064     void Send(const std::vector<char>& Data);
    00065
    00067     // Receive data on the data channel until it is closed
    00069     void Receive(std::vector<char>& Data);
    00070
    00071 private :
    00072
    00074     // Member data
    00076     Ftp&      myFtp;
    00077     SocketTCP myDataSocket;
    00078 };
    00079
    00080
    00084 Ftp::Response::Response(Status Code, const std::string& Message) :
    00085 myStatus (Code),
    00086 myMessage(Message)
    00087 {
    00088
    00089 }
    00090
    00091
    00096 bool Ftp::Response::IsOk() const
    00097 {
    00098     return myStatus < 400;
    00099 }
    00100
    00101
    00105 Ftp::Response::Status Ftp::Response::GetStatus() const
    00106 {
    00107     return myStatus;
    00108 }
    00109
    00110
    00114 const std::string& Ftp::Response::GetMessage() const
    00115 {
    00116     return myMessage;
    00117 }
    00118
    00119
    00123 Ftp::DirectoryResponse::DirectoryResponse(Ftp::Response Resp) :
    00124 Ftp::Response(Resp)
    00125 {
    00126     if (IsOk())
    00127     {
    00128         // Extract the directory from the server response
    00129         std::string::size_type Begin = Resp.GetMessage().find('"', 0);
    00130         std::string::size_type End   = Resp.GetMessage().find('"', Begin + 1);
    00131         myDirectory = Resp.GetMessage().substr(Begin + 1, End - Begin - 1);
    00132     }
    00133 }
    00134
    00135
    00139 const std::string& Ftp::DirectoryResponse::GetDirectory() const
    00140 {
    00141     return myDirectory;
    00142 }
    00143
    00144
    00148 Ftp::ListingResponse::ListingResponse(Ftp::Response Resp, const std::vector<char>& Data) :
    00149 Ftp::Response(Resp)
    00150 {
    00151     if (IsOk())
    00152     {
    00153         // Fill the array of strings
    00154         std::string Paths(Data.begin(), Data.end());
    00155         std::string::size_type LastPos = 0;
    00156         for (std::string::size_type Pos = Paths.find("\r\n"); Pos != std::string::npos; Pos = Paths.find("\r\n", LastPos))
    00157         {
    00158             myFilenames.push_back(Paths.substr(LastPos, Pos - LastPos));
    00159             LastPos = Pos + 2;
    00160         }
    00161     }
    00162 }
    00163
    00164
    00168 std::size_t Ftp::ListingResponse::GetCount() const
    00169 {
    00170     return myFilenames.size();
    00171 }
    00172
    00173
    00177 const std::string& Ftp::ListingResponse::GetFilename(std::size_t Index) const
    00178 {
    00179     return myFilenames[Index];
    00180 }
    00181
    00182
    00186 Ftp::~Ftp()
    00187 {
    00188     Disconnect();
    00189 }
    00190
    00191
    00195 Ftp::Response Ftp::Connect(const IPAddress& Server, unsigned short Port, float Timeout)
    00196 {
    00197     // Connect to the server
    00198     if (myCommandSocket.Connect(Port, Server, Timeout) != Socket::Done)
    00199         return Response(Response::ConnectionFailed);
    00200
    00201     // Get the response to the connection
    00202     return GetResponse();
    00203 }
    00204
    00205
    00209 Ftp::Response Ftp::Login()
    00210 {
    00211     return Login("anonymous", "user@sfml-dev.org");
    00212 }
    00213
    00214
    00218 Ftp::Response Ftp::Login(const std::string& UserName, const std::string& Password)
    00219 {
    00220     Response Resp = SendCommand("USER", UserName);
    00221     if (Resp.IsOk())
    00222         Resp = SendCommand("PASS", Password);
    00223
    00224     return Resp;
    00225 }
    00226
    00227
    00231 Ftp::Response Ftp::Disconnect()
    00232 {
    00233     // Send the exit command
    00234     Response Resp = SendCommand("QUIT");
    00235     if (Resp.IsOk())
    00236         myCommandSocket.Close();
    00237
    00238     return Resp;
    00239 }
    00240
    00241
    00245 Ftp::Response Ftp::KeepAlive()
    00246 {
    00247     return SendCommand("NOOP");
    00248 }
    00249
    00250
    00254 Ftp::DirectoryResponse Ftp::GetWorkingDirectory()
    00255 {
    00256     return DirectoryResponse(SendCommand("PWD"));
    00257 }
    00258
    00259
    00264 Ftp::ListingResponse Ftp::GetDirectoryListing(const std::string& Directory)
    00265 {
    00266     // Open a data channel on default port (20) using ASCII transfer mode
    00267     std::vector<char> DirData;
    00268     DataChannel Data(*this);
    00269     Response Resp = Data.Open(Ascii);
    00270     if (Resp.IsOk())
    00271     {
    00272         // Tell the server to send us the listing
    00273         Resp = SendCommand("NLST", Directory);
    00274         if (Resp.IsOk())
    00275         {
    00276             // Receive the listing
    00277             Data.Receive(DirData);
    00278
    00279             // Get the response from the server
    00280             Resp = GetResponse();
    00281         }
    00282     }
    00283
    00284     return ListingResponse(Resp, DirData);
    00285 }
    00286
    00287
    00291 Ftp::Response Ftp::ChangeDirectory(const std::string& Directory)
    00292 {
    00293     return SendCommand("CWD", Directory);
    00294 }
    00295
    00296
    00300 Ftp::Response Ftp::ParentDirectory()
    00301 {
    00302     return SendCommand("CDUP");
    00303 }
    00304
    00305
    00309 Ftp::Response Ftp::MakeDirectory(const std::string& Name)
    00310 {
    00311     return SendCommand("MKD", Name);
    00312 }
    00313
    00314
    00318 Ftp::Response Ftp::DeleteDirectory(const std::string& Name)
    00319 {
    00320     return SendCommand("RMD", Name);
    00321 }
    00322
    00323
    00327 Ftp::Response Ftp::RenameFile(const std::string& File, const std::string& NewName)
    00328 {
    00329     Response Resp = SendCommand("RNFR", File);
    00330     if (Resp.IsOk())
    00331        Resp = SendCommand("RNTO", NewName);
    00332
    00333     return Resp;
    00334 }
    00335
    00336
    00340 Ftp::Response Ftp::DeleteFile(const std::string& Name)
    00341 {
    00342     return SendCommand("DELE", Name);
    00343 }
    00344
    00345
    00349 Ftp::Response Ftp::Download(const std::string& DistantFile, const std::string& DestPath, TransferMode Mode)
    00350 {
    00351     // Open a data channel using the given transfer mode
    00352     DataChannel Data(*this);
    00353     Response Resp = Data.Open(Mode);
    00354     if (Resp.IsOk())
    00355     {
    00356         // Tell the server to start the transfer
    00357         Resp = SendCommand("RETR", DistantFile);
    00358         if (Resp.IsOk())
    00359         {
    00360             // Receive the file data
    00361             std::vector<char> FileData;
    00362             Data.Receive(FileData);
    00363
    00364             // Get the response from the server
    00365             Resp = GetResponse();
    00366             if (Resp.IsOk())
    00367             {
    00368                 // Extract the filename from the file path
    00369                 std::string Filename = DistantFile;
    00370                 std::string::size_type Pos = Filename.find_last_of("/\\");
    00371                 if (Pos != std::string::npos)
    00372                     Filename = Filename.substr(Pos + 1);
    00373
    00374                 // Make sure the destination path ends with a slash
    00375                 std::string Path = DestPath;
    00376                 if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
    00377                     Path += "/";
    00378
    00379                 // Create the file and copy the received data into it
    00380                 std::ofstream File((Path + Filename).c_str(), std::ios_base::binary);
    00381                 if (!File)
    00382                     return Response(Response::InvalidFile);
    00383                 File.write(&FileData[0], static_cast<std::streamsize>(FileData.size()));
    00384             }
    00385         }
    00386     }
    00387
    00388     return Resp;
    00389 }
    00390
    00391
    00395 Ftp::Response Ftp::Upload(const std::string& LocalFile, const std::string& DestPath, TransferMode Mode)
    00396 {
    00397     // Get the contents of the file to send
    00398     std::ifstream File(LocalFile.c_str(), std::ios_base::binary);
    00399     if (!File)
    00400         return Response(Response::InvalidFile);
    00401     File.seekg(0, std::ios::end);
    00402     std::size_t Length = File.tellg();
    00403     File.seekg(0, std::ios::beg);
    00404     std::vector<char> FileData(Length);
    00405     File.read(&FileData[0], static_cast<std::streamsize>(Length));
    00406
    00407     // Extract the filename from the file path
    00408     std::string Filename = LocalFile;
    00409     std::string::size_type Pos = Filename.find_last_of("/\\");
    00410     if (Pos != std::string::npos)
    00411         Filename = Filename.substr(Pos + 1);
    00412
    00413     // Make sure the destination path ends with a slash
    00414     std::string Path = DestPath;
    00415     if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
    00416         Path += "/";
    00417
    00418     // Open a data channel using the given transfer mode
    00419     DataChannel Data(*this);
    00420     Response Resp = Data.Open(Mode);
    00421     if (Resp.IsOk())
    00422     {
    00423         // Tell the server to start the transfer
    00424         Resp = SendCommand("STOR", Path + Filename);
    00425         if (Resp.IsOk())
    00426         {
    00427             // Send the file data
    00428             Data.Send(FileData);
    00429
    00430             // Get the response from the server
    00431             Resp = GetResponse();
    00432         }
    00433     }
    00434
    00435     return Resp;
    00436 }
    00437
    00438
    00442 Ftp::Response Ftp::SendCommand(const std::string& Command, const std::string& Parameter)
    00443 {
    00444     // Build the command string
    00445     std::string CommandStr;
    00446     if (Parameter != "")
    00447         CommandStr = Command + " " + Parameter + "\r\n";
    00448     else
    00449         CommandStr = Command + "\r\n";
    00450
    00451     // Send it to the server
    00452     if (myCommandSocket.Send(CommandStr.c_str(), CommandStr.length()) != sf::Socket::Done)
    00453         return Response(Response::ConnectionClosed);
    00454
    00455     // Get the response
    00456     return GetResponse();
    00457 }
    00458
    00459
    00464 Ftp::Response Ftp::GetResponse()
    00465 {
    00466     // We'll use a variable to keep track of the last valid code.
    00467     // It is useful in case of multi-lines responses, because the end of such a response
    00468     // will start by the same code
    00469     unsigned int LastCode  = 0;
    00470     bool IsInsideMultiline = false;
    00471     std::string Message;
    00472
    00473     for (;;)
    00474     {
    00475         // Receive the response from the server
    00476         char Buffer[1024];
    00477         std::size_t Length;
    00478         if (myCommandSocket.Receive(Buffer, sizeof(Buffer), Length) != sf::Socket::Done)
    00479             return Response(Response::ConnectionClosed);
    00480
    00481         // There can be several lines inside the received buffer, extract them all
    00482         std::istringstream In(std::string(Buffer, Length), std::ios_base::binary);
    00483         while (In)
    00484         {
    00485             // Try to extract the code
    00486             unsigned int Code;
    00487             if (In >> Code)
    00488             {
    00489                 // Extract the separator
    00490                 char Sep;
    00491                 In.get(Sep);
    00492
    00493                 // The '-' character means a multiline response
    00494                 if ((Sep == '-') && !IsInsideMultiline)
    00495                 {
    00496                     // Set the multiline flag
    00497                     IsInsideMultiline = true;
    00498
    00499                     // Keep track of the code
    00500                     if (LastCode == 0)
    00501                         LastCode = Code;
    00502
    00503                     // Extract the line
    00504                     std::getline(In, Message);
    00505
    00506                     // Remove the ending '\r' (all lines are terminated by "\r\n")
    00507                     Message.erase(Message.length() - 1);
    00508                     Message = Sep + Message + "\n";
    00509                 }
    00510                 else
    00511                 {
    00512                     // We must make sure that the code is the same, otherwise it means
    00513                     // we haven't reached the end of the multiline response
    00514                     if ((Sep != '-') && ((Code == LastCode) || (LastCode == 0)))
    00515                     {
    00516                         // Clear the multiline flag
    00517                         IsInsideMultiline = false;
    00518
    00519                         // Extract the line
    00520                         std::string Line;
    00521                         std::getline(In, Line);
    00522
    00523                         // Remove the ending '\r' (all lines are terminated by "\r\n")
    00524                         Line.erase(Line.length() - 1);
    00525
    00526                         // Append it to the message
    00527                         if (Code == LastCode)
    00528                         {
    00529                             std::ostringstream Out;
    00530                             Out << Code << Sep << Line;
    00531                             Message += Out.str();
    00532                         }
    00533                         else
    00534                         {
    00535                             Message = Sep + Line;
    00536                         }
    00537
    00538                         // Return the response code and message
    00539                         return Response(static_cast<Response::Status>(Code), Message);
    00540                     }
    00541                     else
    00542                     {
    00543                         // The line we just read was actually not a response,
    00544                         // only a new part of the current multiline response
    00545
    00546                         // Extract the line
    00547                         std::string Line;
    00548                         std::getline(In, Line);
    00549
    00550                         if (!Line.empty())
    00551                         {
    00552                             // Remove the ending '\r' (all lines are terminated by "\r\n")
    00553                             Line.erase(Line.length() - 1);
    00554
    00555                             // Append it to the current message
    00556                             std::ostringstream Out;
    00557                             Out << Code << Sep << Line << "\n";
    00558                             Message += Out.str();
    00559                         }
    00560                     }
    00561                 }
    00562             }
    00563             else if (LastCode != 0)
    00564             {
    00565                 // It seems we are in the middle of a multiline response
    00566
    00567                 // Clear the error bits of the stream
    00568                 In.clear();
    00569
    00570                 // Extract the line
    00571                 std::string Line;
    00572                 std::getline(In, Line);
    00573
    00574                 if (!Line.empty())
    00575                 {
    00576                     // Remove the ending '\r' (all lines are terminated by "\r\n")
    00577                     Line.erase(Line.length() - 1);
    00578
    00579                     // Append it to the current message
    00580                     Message += Line + "\n";
    00581                 }
    00582             }
    00583             else
    00584             {
    00585                 // Error : cannot extract the code, and we are not in a multiline response
    00586                 return Response(Response::InvalidResponse);
    00587             }
    00588         }
    00589     }
    00590
    00591     // We never reach there
    00592 }
    00593
    00594
    00598 Ftp::DataChannel::DataChannel(Ftp& Owner) :
    00599 myFtp(Owner)
    00600 {
    00601
    00602 }
    00603
    00604
    00608 Ftp::DataChannel::~DataChannel()
    00609 {
    00610     // Close the data socket
    00611     myDataSocket.Close();
    00612 }
    00613
    00614
    00618 Ftp::Response Ftp::DataChannel::Open(Ftp::TransferMode Mode)
    00619 {
    00620     // Open a data connection in active mode (we connect to the server)
    00621     Ftp::Response Resp = myFtp.SendCommand("PASV");
    00622     if (Resp.IsOk())
    00623     {
    00624         // Extract the connection address and port from the response
    00625         std::string::size_type begin = Resp.GetMessage().find_first_of("0123456789");
    00626         if (begin != std::string::npos)
    00627         {
    00628             sf::Uint8 Data[6] = {0, 0, 0, 0, 0, 0};
    00629             std::string Str = Resp.GetMessage().substr(begin);
    00630             std::size_t Index = 0;
    00631             for (int i = 0; i < 6; ++i)
    00632             {
    00633                 // Extract the current number
    00634                 while (isdigit(Str[Index]))
    00635                 {
    00636                     Data[i] = Data[i] * 10 + (Str[Index] - '0');
    00637                     Index++;
    00638                 }
    00639
    00640                 // Skip separator
    00641                 Index++;
    00642             }
    00643
    00644             // Reconstruct connection port and address
    00645             unsigned short Port = Data[4] * 256 + Data[5];
    00646             sf::IPAddress Address(static_cast<sf::Uint8>(Data[0]),
    00647                                   static_cast<sf::Uint8>(Data[1]),
    00648                                   static_cast<sf::Uint8>(Data[2]),
    00649                                   static_cast<sf::Uint8>(Data[3]));
    00650
    00651             // Connect the data channel to the server
    00652             if (myDataSocket.Connect(Port, Address) == Socket::Done)
    00653             {
    00654                 // Translate the transfer mode to the corresponding FTP parameter
    00655                 std::string ModeStr;
    00656                 switch (Mode)
    00657                 {
    00658                     case Ftp::Binary : ModeStr = "I"; break;
    00659                     case Ftp::Ascii :  ModeStr = "A"; break;
    00660                     case Ftp::Ebcdic : ModeStr = "E"; break;
    00661                 }
    00662
    00663                 // Set the transfer mode
    00664                 Resp = myFtp.SendCommand("TYPE", ModeStr);
    00665             }
    00666             else
    00667             {
    00668                 // Failed to connect to the server
    00669                 Resp = Ftp::Response(Ftp::Response::ConnectionFailed);
    00670             }
    00671         }
    00672     }
    00673
    00674     return Resp;
    00675 }
    00676
    00677
    00681 void Ftp::DataChannel::Receive(std::vector<char>& Data)
    00682 {
    00683     // Receive data
    00684     Data.clear();
    00685     char Buffer[1024];
    00686     std::size_t Received;
    00687     while (myDataSocket.Receive(Buffer, sizeof(Buffer), Received) == sf::Socket::Done)
    00688     {
    00689         std::copy(Buffer, Buffer + Received, std::back_inserter(Data));
    00690     }
    00691
    00692     // Close the data socket
    00693     myDataSocket.Close();
    00694 }
    00695
    00696
    00700 void Ftp::DataChannel::Send(const std::vector<char>& Data)
    00701 {
    00702     // Send data
    00703     myDataSocket.Send(&Data[0], Data.size());
    00704
    00705     // Close the data socket
    00706     myDataSocket.Close();
    00707 }
    00708
    00709 } // namespace sf

:: Copyright © 2007-2008 Laurent Gomila, all rights reserved :: Documentation generated by doxygen 1.5.2 ::