D:/Programmation/Cpp/SFML/src/SFML/AdvancedNetwork/FTP.cpp

00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007 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/AdvancedNetwork/FTP.hpp>
00029 #include <algorithm>
00030 #include <fstream>
00031 #include <iostream>
00032 #include <iterator>
00033 #include <sstream>
00034 
00035 
00039 sfFTP::sfFTP(Listener* FTPListener) :
00040 myListener(FTPListener)
00041 {
00042 
00043 }
00044 
00045 
00049 sfFTP::~sfFTP()
00050 {
00051     Disconnect();
00052 }
00053 
00054 
00058 bool sfFTP::Connect(const sfIPAddress& Server, unsigned int Port)
00059 {
00060     // Check server address
00061     if (!Server.IsValid())
00062     {
00063         std::cerr << "FTP server address is not valid (" << Server << ")" << std::endl;
00064         return false;
00065     }
00066 
00067     // Connect
00068     if (!myCommandSocket.Connect(Port, Server))
00069     {
00070         std::cerr << "Failed to connect to FTP server " << Server << std::endl;
00071         return false;
00072     }
00073 
00074     // Get response to the connection
00075     return GetResponse();
00076 }
00077 
00078 
00082 bool sfFTP::Login(const std::string& UserName, const std::string& Password)
00083 {
00084     return SendCommand("USER", UserName) &&
00085            SendCommand("PASS", Password);
00086 }
00087 
00088 
00092 bool sfFTP::Disconnect()
00093 {
00094     // Send the exit command
00095     if (!SendCommand("QUIT"))
00096         return false;
00097 
00098     // Close the socket
00099     return myCommandSocket.Close();
00100 }
00101 
00102 
00106 bool sfFTP::KeepAlive()
00107 {
00108     return SendCommand("NOOP");
00109 }
00110 
00111 
00115 bool sfFTP::GetWorkingDirectory(std::string& Directory)
00116 {
00117     // Clear the returned string
00118     Directory = "";
00119 
00120     // Tell the server to send us the current working directory
00121     if (!SendCommand("PWD"))
00122         return false;
00123 
00124     // Extract the directory from the server response
00125     std::string::size_type Begin = myLastMessage.find('"', 0);
00126     std::string::size_type End   = myLastMessage.find('"', Begin + 1);
00127     Directory = myLastMessage.substr(Begin + 1, End - Begin - 1);
00128 
00129     return true;
00130 }
00131 
00132 
00137 bool sfFTP::GetDirectoryListing(std::vector<std::string>& Listing, const std::string& Directory)
00138 {
00139     // Clear the listing
00140     Listing.clear();
00141 
00142     // Open a data channel on default port (20) using ASCII transfer mode
00143     DataChannel Data(*this);
00144     if (!Data.Open(Ascii))
00145         return false;
00146 
00147     // Tell the server to send us the listing
00148     if (!SendCommand("NLST", Directory))
00149         return false;
00150 
00151     // Receive the listing
00152     std::vector<char> DirData;
00153     Data.Receive(DirData);
00154 
00155     // Get the response from the server
00156     if (!GetResponse())
00157         return false;
00158 
00159     // Fill the array of strings
00160     std::string Paths(DirData.begin(), DirData.end());
00161     std::string::size_type LastPos = 0;
00162     for (std::string::size_type Pos = Paths.find("\r\n"); Pos != std::string::npos; Pos = Paths.find("\r\n", LastPos))
00163     {
00164         Listing.push_back(Paths.substr(LastPos, Pos - LastPos));
00165         LastPos = Pos + 2;
00166     }
00167 
00168     // A little extra : sort the array :)
00169     std::sort(Listing.begin(), Listing.end());
00170 
00171     return true;
00172 }
00173 
00174 
00178 bool sfFTP::ChangeDirectory(const std::string& Directory)
00179 {
00180     return SendCommand("CWD", Directory);
00181 }
00182 
00183 
00187 bool sfFTP::ParentDirectory()
00188 {
00189     return SendCommand("CDUP");
00190 }
00191 
00192 
00196 bool sfFTP::MakeDirectory(const std::string& Name)
00197 {
00198     return SendCommand("MKD", Name);
00199 }
00200 
00201 
00205 bool sfFTP::DeleteDirectory(const std::string& Name)
00206 {
00207     return SendCommand("RMD", Name);
00208 }
00209 
00210 
00214 bool sfFTP::RenameFile(const std::string& File, const std::string& NewName)
00215 {
00216     return SendCommand("RNFR", File) &&
00217            SendCommand("RNTO", NewName);
00218 }
00219 
00220 
00224 bool sfFTP::RemoveFile(const std::string& Name)
00225 {
00226     return SendCommand("DELE", Name);
00227 }
00228 
00229 
00233 bool sfFTP::Download(const std::string& DistantFile, const std::string& DestPath, TransferMode Mode)
00234 {
00235     // Open a data channel on default port (20) using binary transfer mode
00236     DataChannel Data(*this);
00237     if (!Data.Open(Mode))
00238         return false;
00239 
00240     // Tell the server to start the transfer
00241     if (!SendCommand("RETR", DistantFile))
00242         return false;
00243 
00244     // Receive the file data
00245     std::vector<char> FileData;
00246     Data.Receive(FileData);
00247 
00248     // Get the response from the server
00249     if (!GetResponse())
00250         return false;
00251 
00252     // Extract the filename from the file path
00253     std::string Filename = DistantFile;
00254     std::string::size_type Pos = Filename.find_last_of("/\\");
00255     if (Pos != std::string::npos)
00256         Filename = Filename.substr(Pos + 1);
00257 
00258     // Make sure the destination path ends with a slash
00259     std::string Path = DestPath;
00260     if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
00261         Path += "/";
00262 
00263     // Create the file and copy the received data into it
00264     std::ofstream File((Path + Filename).c_str(), std::ios_base::binary);
00265     File.write(&FileData[0], static_cast<std::streamsize>(FileData.size()));
00266 
00267     return true;
00268 }
00269 
00270 
00274 bool sfFTP::Upload(const std::string& LocalFile, const std::string& DestPath, TransferMode Mode)
00275 {
00276     // Get the contents of the file to send
00277     std::ifstream File(LocalFile.c_str(), std::ios_base::binary);
00278     File.seekg(0, std::ios::end);
00279     std::size_t Length = File.tellg();
00280     File.seekg(0, std::ios::beg);
00281     std::vector<char> FileData(Length);
00282     File.read(&FileData[0], static_cast<std::streamsize>(Length));
00283 
00284     // Extract the filename from the file path
00285     std::string Filename = LocalFile;
00286     std::string::size_type Pos = Filename.find_last_of("/\\");
00287     if (Pos != std::string::npos)
00288         Filename = Filename.substr(Pos + 1);
00289 
00290     // Make sure the destination path ends with a slash
00291     std::string Path = DestPath;
00292     if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
00293         Path += "/";
00294 
00295     // Open a data channel on default port (20) using binary transfer mode
00296     DataChannel Data(*this);
00297     if (!Data.Open(Mode))
00298         return false;
00299 
00300     // Tell the server to start the transfer
00301     if (!SendCommand("STOR", Path + Filename))
00302         return false;
00303 
00304     // Send the file data
00305     Data.Send(FileData);
00306 
00307     // Get the response from the server
00308     if (!GetResponse())
00309         return false;
00310 
00311     return true;
00312 }
00313 
00314 
00318 bool sfFTP::SendCommand(const std::string& Command, const std::string& Parameter)
00319 {
00320     // Build the command string
00321     std::string CommandStr;
00322     if (Parameter != "")
00323         CommandStr = Command + " " + Parameter + "\r\n";
00324     else
00325         CommandStr = Command + "\r\n";
00326 
00327     // Send it to the server
00328     if (!myCommandSocket.Send(CommandStr.c_str(), CommandStr.length()))
00329     {
00330         std::cerr << "FTP connection closed " << std::endl;
00331         return false;
00332     }
00333 
00334     // Get the response
00335     return GetResponse();
00336 }
00337 
00338 
00343 bool sfFTP::GetResponse()
00344 {
00345     // We'll use a variable to keep track of the last valid code.
00346     // It is useful in case of multi-lines responses, because the end of such a response
00347     // will start by the same code
00348     unsigned int LastCode  = 0;
00349     bool IsInsideMultiline = false;
00350 
00351     while (true)
00352     {
00353         // Receive the response from the server
00354         char Buffer[1024];
00355         std::size_t Length;
00356         if (!myCommandSocket.Receive(Buffer, sizeof(Buffer), Length))
00357         {
00358             std::cerr << "FTP connection closed " << std::endl;
00359             return false;
00360         }
00361 
00362         // There can be several lines inside the received buffer, extract them all
00363         std::istringstream iss(std::string(Buffer, Length), std::ios_base::binary);
00364         while (iss)
00365         {
00366             // Try to extract the code
00367             unsigned int Code;
00368             if (iss >> Code)
00369             {
00370                 // Extract the separator
00371                 char Sep;
00372                 iss.get(Sep);
00373 
00374                 // The '-' character means a multiline response
00375                 if ((Sep == '-') && !IsInsideMultiline)
00376                 {
00377                     // Set the multiline flag
00378                     IsInsideMultiline = true;
00379 
00380                     // Keep track of the code
00381                     if (LastCode == 0)
00382                         LastCode = Code;
00383 
00384                     // Extract the line
00385                     std::getline(iss, myLastMessage);
00386 
00387                     // Remove the ending '\r' (all lines are terminated by "\r\n")
00388                     myLastMessage.erase(myLastMessage.length() - 1);
00389                     myLastMessage = Sep + myLastMessage + "\n";
00390                 }
00391                 else
00392                 {
00393                     // We must make sure that the code is the same, otherwise it means
00394                     // we haven't reached the end of the multiline response
00395                     if ((Sep != '-') && ((Code == LastCode) || (LastCode == 0)))
00396                     {
00397                         // Clear the multiline flag
00398                         IsInsideMultiline = false;
00399 
00400                         // Extract the line
00401                         std::string Line;
00402                         std::getline(iss, Line);
00403 
00404                         // Remove the ending '\r' (all lines are terminated by "\r\n")
00405                         Line.erase(Line.length() - 1);
00406 
00407                         // Append it to the message
00408                         if (Code == LastCode)
00409                         {
00410                             std::ostringstream oss;
00411                             oss << Code << Sep << Line;
00412                             myLastMessage += oss.str();
00413                         }
00414                         else
00415                         {
00416                             myLastMessage = Sep + Line;
00417                         }
00418 
00419                         // Forward the code and message to the listener
00420                         if (myListener)
00421                             myListener->OnResponse(Code, myLastMessage);
00422 
00423                         // All codes above 400 (4xx and 5xx families) are error codes
00424                         return Code < 400;
00425                     }
00426                     else
00427                     {
00428                         // The line we just read was actually not a response,
00429                         // only a new part of the current multiline response
00430 
00431                         // Extract the line
00432                         std::string Line;
00433                         std::getline(iss, Line);
00434 
00435                         if (!Line.empty())
00436                         {
00437                             // Remove the ending '\r' (all lines are terminated by "\r\n")
00438                             Line.erase(Line.length() - 1);
00439 
00440                             // Append it to the current message
00441                             std::ostringstream oss;
00442                             oss << Code << Sep << Line << "\n";
00443                             myLastMessage += oss.str();
00444                         }
00445                     }
00446                 }
00447             }
00448             else if (LastCode != 0)
00449             {
00450                 // It seems we are in the middle of a multiline response
00451 
00452                 // Clear the error bits of the stream
00453                 iss.clear();
00454 
00455                 // Extract the line
00456                 std::string Line;
00457                 std::getline(iss, Line);
00458 
00459                 if (!Line.empty())
00460                 {
00461                     // Remove the ending '\r' (all lines are terminated by "\r\n")
00462                     Line.erase(Line.length() - 1);
00463 
00464                     // Append it to the current message
00465                     myLastMessage += Line + "\n";
00466                 }
00467             }
00468             else
00469             {
00470                 // Error : cannot extract the code, and we are not in a multiline response
00471                 std::cerr << "Invalid response from the FTP server" << std::endl;
00472                 return false;
00473             }
00474         }
00475     }
00476 
00477     return false;
00478 }
00479 
00480 
00484 sfFTP::DataChannel::DataChannel(sfFTP& FTP) :
00485 myFTP(FTP)
00486 {
00487 
00488 }
00489 
00490 
00494 sfFTP::DataChannel::~DataChannel()
00495 {
00496     // Close the data socket
00497     myDataSocket.Close();
00498 }
00499 
00500 
00504 bool sfFTP::DataChannel::Open(sfFTP::TransferMode Mode, unsigned short Port)
00505 {
00506     // Setup the data socket, which will be used to transfer the file
00507     if (!myDataSocket.Listen(Port))
00508         return false;
00509 
00510     // Build the string containing our IP address and the port to use
00511     unsigned short P1 = Port / 256;
00512     unsigned short P2 = Port % 256;
00513     sfIPAddress Address = sfIPAddress::GetLocalAddress();
00514     std::ostringstream oss;
00515     oss << Address << "." << P1 << "." << P2;
00516     std::string Parameter = oss.str();
00517     std::replace(Parameter.begin(), Parameter.end(), '.', ',');
00518 
00519     // Tell the server to open a data channel in active mode
00520     if (!myFTP.SendCommand("PORT", Parameter))
00521         return false;
00522 
00523     // Translate the transfer mode to the corresponding FTP parameter
00524     std::string ModeStr;
00525     switch (Mode)
00526     {
00527         case sfFTP::Binary : ModeStr = "I"; break;
00528         case sfFTP::Ascii :  ModeStr = "A"; break;
00529         case sfFTP::Ebcdic : ModeStr = "E"; break;
00530     }
00531 
00532     // Set the transfer mode
00533     if (!myFTP.SendCommand("TYPE", ModeStr))
00534         return false;
00535 
00536     return true;
00537 }
00538 
00539 
00543 void sfFTP::DataChannel::Receive(std::vector<char>& Data)
00544 {
00545     // Accept the server connection
00546     sfSocketTCP TransferSocket = myDataSocket.Accept();
00547 
00548     // Receive the data
00549     Data.clear();
00550     char Buffer[1024];
00551     std::size_t Received;
00552     while (TransferSocket.Receive(Buffer, sizeof(Buffer), Received))
00553     {
00554         std::copy(Buffer, Buffer + Received, std::back_inserter(Data));
00555     }
00556 
00557     // Close the transfer socket
00558     TransferSocket.Close();
00559 }
00560 
00561 
00565 void sfFTP::DataChannel::Send(const std::vector<char>& Data)
00566 {
00567     // Accept the server connection
00568     sfSocketTCP TransferSocket = myDataSocket.Accept();
00569 
00570     // Send the data
00571     TransferSocket.Send(&Data[0], Data.size());
00572 
00573     // Close the transfer socket
00574     TransferSocket.Close();
00575 }

Generated for SFML by  doxygen 1.5.2