- 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                 if (!FileData.empty())
    00384                     File.write(&FileData[0], static_cast<std::streamsize>(FileData.size()));
    00385             }
    00386         }
    00387     }
    00388
    00389     return Resp;
    00390 }
    00391
    00392
    00396 Ftp::Response Ftp::Upload(const std::string& LocalFile, const std::string& DestPath, TransferMode Mode)
    00397 {
    00398     // Get the contents of the file to send
    00399     std::ifstream File(LocalFile.c_str(), std::ios_base::binary);
    00400     if (!File)
    00401         return Response(Response::InvalidFile);
    00402     File.seekg(0, std::ios::end);
    00403     std::size_t Length = File.tellg();
    00404     File.seekg(0, std::ios::beg);
    00405     std::vector<char> FileData(Length);
    00406     if (Length > 0)
    00407         File.read(&FileData[0], static_cast<std::streamsize>(Length));
    00408
    00409     // Extract the filename from the file path
    00410     std::string Filename = LocalFile;
    00411     std::string::size_type Pos = Filename.find_last_of("/\\");
    00412     if (Pos != std::string::npos)
    00413         Filename = Filename.substr(Pos + 1);
    00414
    00415     // Make sure the destination path ends with a slash
    00416     std::string Path = DestPath;
    00417     if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
    00418         Path += "/";
    00419
    00420     // Open a data channel using the given transfer mode
    00421     DataChannel Data(*this);
    00422     Response Resp = Data.Open(Mode);
    00423     if (Resp.IsOk())
    00424     {
    00425         // Tell the server to start the transfer
    00426         Resp = SendCommand("STOR", Path + Filename);
    00427         if (Resp.IsOk())
    00428         {
    00429             // Send the file data
    00430             Data.Send(FileData);
    00431
    00432             // Get the response from the server
    00433             Resp = GetResponse();
    00434         }
    00435     }
    00436
    00437     return Resp;
    00438 }
    00439
    00440
    00444 Ftp::Response Ftp::SendCommand(const std::string& Command, const std::string& Parameter)
    00445 {
    00446     // Build the command string
    00447     std::string CommandStr;
    00448     if (Parameter != "")
    00449         CommandStr = Command + " " + Parameter + "\r\n";
    00450     else
    00451         CommandStr = Command + "\r\n";
    00452
    00453     // Send it to the server
    00454     if (myCommandSocket.Send(CommandStr.c_str(), CommandStr.length()) != sf::Socket::Done)
    00455         return Response(Response::ConnectionClosed);
    00456
    00457     // Get the response
    00458     return GetResponse();
    00459 }
    00460
    00461
    00466 Ftp::Response Ftp::GetResponse()
    00467 {
    00468     // We'll use a variable to keep track of the last valid code.
    00469     // It is useful in case of multi-lines responses, because the end of such a response
    00470     // will start by the same code
    00471     unsigned int LastCode  = 0;
    00472     bool IsInsideMultiline = false;
    00473     std::string Message;
    00474
    00475     for (;;)
    00476     {
    00477         // Receive the response from the server
    00478         char Buffer[1024];
    00479         std::size_t Length;
    00480         if (myCommandSocket.Receive(Buffer, sizeof(Buffer), Length) != sf::Socket::Done)
    00481             return Response(Response::ConnectionClosed);
    00482
    00483         // There can be several lines inside the received buffer, extract them all
    00484         std::istringstream In(std::string(Buffer, Length), std::ios_base::binary);
    00485         while (In)
    00486         {
    00487             // Try to extract the code
    00488             unsigned int Code;
    00489             if (In >> Code)
    00490             {
    00491                 // Extract the separator
    00492                 char Sep;
    00493                 In.get(Sep);
    00494
    00495                 // The '-' character means a multiline response
    00496                 if ((Sep == '-') && !IsInsideMultiline)
    00497                 {
    00498                     // Set the multiline flag
    00499                     IsInsideMultiline = true;
    00500
    00501                     // Keep track of the code
    00502                     if (LastCode == 0)
    00503                         LastCode = Code;
    00504
    00505                     // Extract the line
    00506                     std::getline(In, Message);
    00507
    00508                     // Remove the ending '\r' (all lines are terminated by "\r\n")
    00509                     Message.erase(Message.length() - 1);
    00510                     Message = Sep + Message + "\n";
    00511                 }
    00512                 else
    00513                 {
    00514                     // We must make sure that the code is the same, otherwise it means
    00515                     // we haven't reached the end of the multiline response
    00516                     if ((Sep != '-') && ((Code == LastCode) || (LastCode == 0)))
    00517                     {
    00518                         // Clear the multiline flag
    00519                         IsInsideMultiline = false;
    00520
    00521                         // Extract the line
    00522                         std::string Line;
    00523                         std::getline(In, Line);
    00524
    00525                         // Remove the ending '\r' (all lines are terminated by "\r\n")
    00526                         Line.erase(Line.length() - 1);
    00527
    00528                         // Append it to the message
    00529                         if (Code == LastCode)
    00530                         {
    00531                             std::ostringstream Out;
    00532                             Out << Code << Sep << Line;
    00533                             Message += Out.str();
    00534                         }
    00535                         else
    00536                         {
    00537                             Message = Sep + Line;
    00538                         }
    00539
    00540                         // Return the response code and message
    00541                         return Response(static_cast<Response::Status>(Code), Message);
    00542                     }
    00543                     else
    00544                     {
    00545                         // The line we just read was actually not a response,
    00546                         // only a new part of the current multiline response
    00547
    00548                         // Extract the line
    00549                         std::string Line;
    00550                         std::getline(In, Line);
    00551
    00552                         if (!Line.empty())
    00553                         {
    00554                             // Remove the ending '\r' (all lines are terminated by "\r\n")
    00555                             Line.erase(Line.length() - 1);
    00556
    00557                             // Append it to the current message
    00558                             std::ostringstream Out;
    00559                             Out << Code << Sep << Line << "\n";
    00560                             Message += Out.str();
    00561                         }
    00562                     }
    00563                 }
    00564             }
    00565             else if (LastCode != 0)
    00566             {
    00567                 // It seems we are in the middle of a multiline response
    00568
    00569                 // Clear the error bits of the stream
    00570                 In.clear();
    00571
    00572                 // Extract the line
    00573                 std::string Line;
    00574                 std::getline(In, Line);
    00575
    00576                 if (!Line.empty())
    00577                 {
    00578                     // Remove the ending '\r' (all lines are terminated by "\r\n")
    00579                     Line.erase(Line.length() - 1);
    00580
    00581                     // Append it to the current message
    00582                     Message += Line + "\n";
    00583                 }
    00584             }
    00585             else
    00586             {
    00587                 // Error : cannot extract the code, and we are not in a multiline response
    00588                 return Response(Response::InvalidResponse);
    00589             }
    00590         }
    00591     }
    00592
    00593     // We never reach there
    00594 }
    00595
    00596
    00600 Ftp::DataChannel::DataChannel(Ftp& Owner) :
    00601 myFtp(Owner)
    00602 {
    00603
    00604 }
    00605
    00606
    00610 Ftp::DataChannel::~DataChannel()
    00611 {
    00612     // Close the data socket
    00613     myDataSocket.Close();
    00614 }
    00615
    00616
    00620 Ftp::Response Ftp::DataChannel::Open(Ftp::TransferMode Mode)
    00621 {
    00622     // Open a data connection in active mode (we connect to the server)
    00623     Ftp::Response Resp = myFtp.SendCommand("PASV");
    00624     if (Resp.IsOk())
    00625     {
    00626         // Extract the connection address and port from the response
    00627         std::string::size_type begin = Resp.GetMessage().find_first_of("0123456789");
    00628         if (begin != std::string::npos)
    00629         {
    00630             sf::Uint8 Data[6] = {0, 0, 0, 0, 0, 0};
    00631             std::string Str = Resp.GetMessage().substr(begin);
    00632             std::size_t Index = 0;
    00633             for (int i = 0; i < 6; ++i)
    00634             {
    00635                 // Extract the current number
    00636                 while (isdigit(Str[Index]))
    00637                 {
    00638                     Data[i] = Data[i] * 10 + (Str[Index] - '0');
    00639                     Index++;
    00640                 }
    00641
    00642                 // Skip separator
    00643                 Index++;
    00644             }
    00645
    00646             // Reconstruct connection port and address
    00647             unsigned short Port = Data[4] * 256 + Data[5];
    00648             sf::IPAddress Address(static_cast<sf::Uint8>(Data[0]),
    00649                                   static_cast<sf::Uint8>(Data[1]),
    00650                                   static_cast<sf::Uint8>(Data[2]),
    00651                                   static_cast<sf::Uint8>(Data[3]));
    00652
    00653             // Connect the data channel to the server
    00654             if (myDataSocket.Connect(Port, Address) == Socket::Done)
    00655             {
    00656                 // Translate the transfer mode to the corresponding FTP parameter
    00657                 std::string ModeStr;
    00658                 switch (Mode)
    00659                 {
    00660                     case Ftp::Binary : ModeStr = "I"; break;
    00661                     case Ftp::Ascii :  ModeStr = "A"; break;
    00662                     case Ftp::Ebcdic : ModeStr = "E"; break;
    00663                 }
    00664
    00665                 // Set the transfer mode
    00666                 Resp = myFtp.SendCommand("TYPE", ModeStr);
    00667             }
    00668             else
    00669             {
    00670                 // Failed to connect to the server
    00671                 Resp = Ftp::Response(Ftp::Response::ConnectionFailed);
    00672             }
    00673         }
    00674     }
    00675
    00676     return Resp;
    00677 }
    00678
    00679
    00683 void Ftp::DataChannel::Receive(std::vector<char>& Data)
    00684 {
    00685     // Receive data
    00686     Data.clear();
    00687     char Buffer[1024];
    00688     std::size_t Received;
    00689     while (myDataSocket.Receive(Buffer, sizeof(Buffer), Received) == sf::Socket::Done)
    00690     {
    00691         std::copy(Buffer, Buffer + Received, std::back_inserter(Data));
    00692     }
    00693
    00694     // Close the data socket
    00695     myDataSocket.Close();
    00696 }
    00697
    00698
    00702 void Ftp::DataChannel::Send(const std::vector<char>& Data)
    00703 {
    00704     // Send data
    00705     if (!Data.empty())
    00706         myDataSocket.Send(&Data[0], Data.size());
    00707
    00708     // Close the data socket
    00709     myDataSocket.Close();
    00710 }
    00711
    00712 } // namespace sf

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