Bug Summary

File:Externals/SFML/src/SFML/Network/Ftp.cpp
Location:line 519, column 25
Description:Value stored to 'IsInsideMultiline' is never read

Annotated Source Code

1////////////////////////////////////////////////////////////
2//
3// SFML - Simple and Fast Multimedia Library
4// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
23////////////////////////////////////////////////////////////
24
25////////////////////////////////////////////////////////////
26// Headers
27////////////////////////////////////////////////////////////
28#include <SFML/Network/Ftp.hpp>
29#include <SFML/Network/IPAddress.hpp>
30#include <algorithm>
31#include <fstream>
32#include <iterator>
33#include <sstream>
34
35
36namespace sf
37{
38////////////////////////////////////////////////////////////
39// Utility class for exchanging stuff with the server
40// on the data channel
41////////////////////////////////////////////////////////////
42class Ftp::DataChannel : NonCopyable
43{
44public :
45
46 ////////////////////////////////////////////////////////////
47 // Constructor
48 ////////////////////////////////////////////////////////////
49 DataChannel(Ftp& Owner);
50
51 ////////////////////////////////////////////////////////////
52 // Destructor
53 ////////////////////////////////////////////////////////////
54 ~DataChannel();
55
56 ////////////////////////////////////////////////////////////
57 // Open the data channel using the specified mode and port
58 ////////////////////////////////////////////////////////////
59 Ftp::Response Open(Ftp::TransferMode Mode);
60
61 ////////////////////////////////////////////////////////////
62 // Send data on the data channel
63 ////////////////////////////////////////////////////////////
64 void Send(const std::vector<char>& Data);
65
66 ////////////////////////////////////////////////////////////
67 // Receive data on the data channel until it is closed
68 ////////////////////////////////////////////////////////////
69 void Receive(std::vector<char>& Data);
70
71private :
72
73 ////////////////////////////////////////////////////////////
74 // Member data
75 ////////////////////////////////////////////////////////////
76 Ftp& myFtp; ///< Reference to the owner Ftp instance
77 SocketTCP myDataSocket; ///< Socket used for data transfers
78};
79
80
81////////////////////////////////////////////////////////////
82/// Default constructor
83////////////////////////////////////////////////////////////
84Ftp::Response::Response(Status Code, const std::string& Message) :
85myStatus (Code),
86myMessage(Message)
87{
88
89}
90
91
92////////////////////////////////////////////////////////////
93/// Convenience function to check if the response status code
94/// means a success
95////////////////////////////////////////////////////////////
96bool Ftp::Response::IsOk() const
97{
98 return myStatus < 400;
99}
100
101
102////////////////////////////////////////////////////////////
103/// Get the response status code
104////////////////////////////////////////////////////////////
105Ftp::Response::Status Ftp::Response::GetStatus() const
106{
107 return myStatus;
108}
109
110
111////////////////////////////////////////////////////////////
112/// Get the full message contained in the response
113////////////////////////////////////////////////////////////
114const std::string& Ftp::Response::GetMessage() const
115{
116 return myMessage;
117}
118
119
120////////////////////////////////////////////////////////////
121/// Default constructor
122////////////////////////////////////////////////////////////
123Ftp::DirectoryResponse::DirectoryResponse(Ftp::Response Resp) :
124Ftp::Response(Resp)
125{
126 if (IsOk())
127 {
128 // Extract the directory from the server response
129 std::string::size_type Begin = Resp.GetMessage().find('"', 0);
130 std::string::size_type End = Resp.GetMessage().find('"', Begin + 1);
131 myDirectory = Resp.GetMessage().substr(Begin + 1, End - Begin - 1);
132 }
133}
134
135
136////////////////////////////////////////////////////////////
137/// Get the directory returned in the response
138////////////////////////////////////////////////////////////
139const std::string& Ftp::DirectoryResponse::GetDirectory() const
140{
141 return myDirectory;
142}
143
144
145////////////////////////////////////////////////////////////
146/// Default constructor
147////////////////////////////////////////////////////////////
148Ftp::ListingResponse::ListingResponse(Ftp::Response Resp, const std::vector<char>& Data) :
149Ftp::Response(Resp)
150{
151 if (IsOk())
152 {
153 // Fill the array of strings
154 std::string Paths(Data.begin(), Data.end());
155 std::string::size_type LastPos = 0;
156 for (std::string::size_type Pos = Paths.find("\r\n"); Pos != std::string::npos; Pos = Paths.find("\r\n", LastPos))
157 {
158 myFilenames.push_back(Paths.substr(LastPos, Pos - LastPos));
159 LastPos = Pos + 2;
160 }
161 }
162}
163
164
165////////////////////////////////////////////////////////////
166/// Get the number of filenames in the listing
167////////////////////////////////////////////////////////////
168std::size_t Ftp::ListingResponse::GetCount() const
169{
170 return myFilenames.size();
171}
172
173
174////////////////////////////////////////////////////////////
175/// Get the Index-th filename in the directory
176////////////////////////////////////////////////////////////
177const std::string& Ftp::ListingResponse::GetFilename(std::size_t Index) const
178{
179 return myFilenames[Index];
180}
181
182
183////////////////////////////////////////////////////////////
184/// Destructor -- close the connection with the server
185////////////////////////////////////////////////////////////
186Ftp::~Ftp()
187{
188 Disconnect();
189}
190
191
192////////////////////////////////////////////////////////////
193/// Connect to the specified FTP server
194////////////////////////////////////////////////////////////
195Ftp::Response Ftp::Connect(const IPAddress& Server, unsigned short Port, float Timeout)
196{
197 // Connect to the server
198 if (myCommandSocket.Connect(Port, Server, Timeout) != Socket::Done)
199 return Response(Response::ConnectionFailed);
200
201 // Get the response to the connection
202 return GetResponse();
203}
204
205
206////////////////////////////////////////////////////////////
207/// Log in using anonymous account
208////////////////////////////////////////////////////////////
209Ftp::Response Ftp::Login()
210{
211 return Login("anonymous", "user@sfml-dev.org");
212}
213
214
215////////////////////////////////////////////////////////////
216/// Log in using a username and a password
217////////////////////////////////////////////////////////////
218Ftp::Response Ftp::Login(const std::string& UserName, const std::string& Password)
219{
220 Response Resp = SendCommand("USER", UserName);
221 if (Resp.IsOk())
222 Resp = SendCommand("PASS", Password);
223
224 return Resp;
225}
226
227
228////////////////////////////////////////////////////////////
229/// Close the connection with FTP server
230////////////////////////////////////////////////////////////
231Ftp::Response Ftp::Disconnect()
232{
233 // Send the exit command
234 Response Resp = SendCommand("QUIT");
235 if (Resp.IsOk())
236 myCommandSocket.Close();
237
238 return Resp;
239}
240
241
242////////////////////////////////////////////////////////////
243/// Send a null command just to prevent from being disconnected
244////////////////////////////////////////////////////////////
245Ftp::Response Ftp::KeepAlive()
246{
247 return SendCommand("NOOP");
248}
249
250
251////////////////////////////////////////////////////////////
252/// Get the current working directory
253////////////////////////////////////////////////////////////
254Ftp::DirectoryResponse Ftp::GetWorkingDirectory()
255{
256 return DirectoryResponse(SendCommand("PWD"));
257}
258
259
260////////////////////////////////////////////////////////////
261/// Get the contents of the given directory
262/// (subdirectories and files)
263////////////////////////////////////////////////////////////
264Ftp::ListingResponse Ftp::GetDirectoryListing(const std::string& Directory)
265{
266 // Open a data channel on default port (20) using ASCII transfer mode
267 std::vector<char> DirData;
268 DataChannel Data(*this);
269 Response Resp = Data.Open(Ascii);
270 if (Resp.IsOk())
271 {
272 // Tell the server to send us the listing
273 Resp = SendCommand("NLST", Directory);
274 if (Resp.IsOk())
275 {
276 // Receive the listing
277 Data.Receive(DirData);
278
279 // Get the response from the server
280 Resp = GetResponse();
281 }
282 }
283
284 return ListingResponse(Resp, DirData);
285}
286
287
288////////////////////////////////////////////////////////////
289/// Change the current working directory
290////////////////////////////////////////////////////////////
291Ftp::Response Ftp::ChangeDirectory(const std::string& Directory)
292{
293 return SendCommand("CWD", Directory);
294}
295
296
297////////////////////////////////////////////////////////////
298/// Go to the parent directory of the current one
299////////////////////////////////////////////////////////////
300Ftp::Response Ftp::ParentDirectory()
301{
302 return SendCommand("CDUP");
303}
304
305
306////////////////////////////////////////////////////////////
307/// Create a new directory
308////////////////////////////////////////////////////////////
309Ftp::Response Ftp::MakeDirectory(const std::string& Name)
310{
311 return SendCommand("MKD", Name);
312}
313
314
315////////////////////////////////////////////////////////////
316/// Remove an existing directory
317////////////////////////////////////////////////////////////
318Ftp::Response Ftp::DeleteDirectory(const std::string& Name)
319{
320 return SendCommand("RMD", Name);
321}
322
323
324////////////////////////////////////////////////////////////
325/// Rename a file
326////////////////////////////////////////////////////////////
327Ftp::Response Ftp::RenameFile(const std::string& File, const std::string& NewName)
328{
329 Response Resp = SendCommand("RNFR", File);
330 if (Resp.IsOk())
331 Resp = SendCommand("RNTO", NewName);
332
333 return Resp;
334}
335
336
337////////////////////////////////////////////////////////////
338/// Remove an existing file
339////////////////////////////////////////////////////////////
340Ftp::Response Ftp::DeleteFile(const std::string& Name)
341{
342 return SendCommand("DELE", Name);
343}
344
345
346////////////////////////////////////////////////////////////
347/// Download a file from the server
348////////////////////////////////////////////////////////////
349Ftp::Response Ftp::Download(const std::string& DistantFile, const std::string& DestPath, TransferMode Mode)
350{
351 // Open a data channel using the given transfer mode
352 DataChannel Data(*this);
353 Response Resp = Data.Open(Mode);
354 if (Resp.IsOk())
355 {
356 // Tell the server to start the transfer
357 Resp = SendCommand("RETR", DistantFile);
358 if (Resp.IsOk())
359 {
360 // Receive the file data
361 std::vector<char> FileData;
362 Data.Receive(FileData);
363
364 // Get the response from the server
365 Resp = GetResponse();
366 if (Resp.IsOk())
367 {
368 // Extract the filename from the file path
369 std::string Filename = DistantFile;
370 std::string::size_type Pos = Filename.find_last_of("/\\");
371 if (Pos != std::string::npos)
372 Filename = Filename.substr(Pos + 1);
373
374 // Make sure the destination path ends with a slash
375 std::string Path = DestPath;
376 if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
377 Path += "/";
378
379 // Create the file and copy the received data into it
380 std::ofstream File((Path + Filename).c_str(), std::ios_base::binary);
381 if (!File)
382 return Response(Response::InvalidFile);
383 if (!FileData.empty())
384 File.write(&FileData[0], static_cast<std::streamsize>(FileData.size()));
385 }
386 }
387 }
388
389 return Resp;
390}
391
392
393////////////////////////////////////////////////////////////
394/// Upload a file to the server
395////////////////////////////////////////////////////////////
396Ftp::Response Ftp::Upload(const std::string& LocalFile, const std::string& DestPath, TransferMode Mode)
397{
398 // Get the contents of the file to send
399 std::ifstream File(LocalFile.c_str(), std::ios_base::binary);
400 if (!File)
401 return Response(Response::InvalidFile);
402 File.seekg(0, std::ios::end);
403 std::size_t Length = File.tellg();
404 File.seekg(0, std::ios::beg);
405 std::vector<char> FileData(Length);
406 if (Length > 0)
407 File.read(&FileData[0], static_cast<std::streamsize>(Length));
408
409 // Extract the filename from the file path
410 std::string Filename = LocalFile;
411 std::string::size_type Pos = Filename.find_last_of("/\\");
412 if (Pos != std::string::npos)
413 Filename = Filename.substr(Pos + 1);
414
415 // Make sure the destination path ends with a slash
416 std::string Path = DestPath;
417 if (!Path.empty() && (Path[Path.size() - 1] != '\\') && (Path[Path.size() - 1] != '/'))
418 Path += "/";
419
420 // Open a data channel using the given transfer mode
421 DataChannel Data(*this);
422 Response Resp = Data.Open(Mode);
423 if (Resp.IsOk())
424 {
425 // Tell the server to start the transfer
426 Resp = SendCommand("STOR", Path + Filename);
427 if (Resp.IsOk())
428 {
429 // Send the file data
430 Data.Send(FileData);
431
432 // Get the response from the server
433 Resp = GetResponse();
434 }
435 }
436
437 return Resp;
438}
439
440
441////////////////////////////////////////////////////////////
442/// Send a command to the FTP server
443////////////////////////////////////////////////////////////
444Ftp::Response Ftp::SendCommand(const std::string& Command, const std::string& Parameter)
445{
446 // Build the command string
447 std::string CommandStr;
448 if (Parameter != "")
449 CommandStr = Command + " " + Parameter + "\r\n";
450 else
451 CommandStr = Command + "\r\n";
452
453 // Send it to the server
454 if (myCommandSocket.Send(CommandStr.c_str(), CommandStr.length()) != sf::Socket::Done)
455 return Response(Response::ConnectionClosed);
456
457 // Get the response
458 return GetResponse();
459}
460
461
462////////////////////////////////////////////////////////////
463/// Receive a response from the server
464/// (usually after a command has been sent)
465////////////////////////////////////////////////////////////
466Ftp::Response Ftp::GetResponse()
467{
468 // We'll use a variable to keep track of the last valid code.
469 // It is useful in case of multi-lines responses, because the end of such a response
470 // will start by the same code
471 unsigned int LastCode = 0;
472 bool IsInsideMultiline = false;
473 std::string Message;
474
475 for (;;)
476 {
477 // Receive the response from the server
478 char Buffer[1024];
479 std::size_t Length;
480 if (myCommandSocket.Receive(Buffer, sizeof(Buffer), Length) != sf::Socket::Done)
481 return Response(Response::ConnectionClosed);
482
483 // There can be several lines inside the received buffer, extract them all
484 std::istringstream In(std::string(Buffer, Length), std::ios_base::binary);
485 while (In)
486 {
487 // Try to extract the code
488 unsigned int Code;
489 if (In >> Code)
490 {
491 // Extract the separator
492 char Sep = 0;
493 In.get(Sep);
494
495 // The '-' character means a multiline response
496 if ((Sep == '-') && !IsInsideMultiline)
497 {
498 // Set the multiline flag
499 IsInsideMultiline = true;
500
501 // Keep track of the code
502 if (LastCode == 0)
503 LastCode = Code;
504
505 // Extract the line
506 std::getline(In, Message);
507
508 // Remove the ending '\r' (all lines are terminated by "\r\n")
509 Message.erase(Message.length() - 1);
510 Message = Sep + Message + "\n";
511 }
512 else
513 {
514 // We must make sure that the code is the same, otherwise it means
515 // we haven't reached the end of the multiline response
516 if ((Sep != '-') && ((Code == LastCode) || (LastCode == 0)))
517 {
518 // Clear the multiline flag
519 IsInsideMultiline = false;
Value stored to 'IsInsideMultiline' is never read
520
521 // Extract the line
522 std::string Line;
523 std::getline(In, Line);
524
525 // Remove the ending '\r' (all lines are terminated by "\r\n")
526 Line.erase(Line.length() - 1);
527
528 // Append it to the message
529 if (Code == LastCode)
530 {
531 std::ostringstream Out;
532 Out << Code << Sep << Line;
533 Message += Out.str();
534 }
535 else
536 {
537 Message = Sep + Line;
538 }
539
540 // Return the response code and message
541 return Response(static_cast<Response::Status>(Code), Message);
542 }
543 else
544 {
545 // The line we just read was actually not a response,
546 // only a new part of the current multiline response
547
548 // Extract the line
549 std::string Line;
550 std::getline(In, Line);
551
552 if (!Line.empty())
553 {
554 // Remove the ending '\r' (all lines are terminated by "\r\n")
555 Line.erase(Line.length() - 1);
556
557 // Append it to the current message
558 std::ostringstream Out;
559 Out << Code << Sep << Line << "\n";
560 Message += Out.str();
561 }
562 }
563 }
564 }
565 else if (LastCode != 0)
566 {
567 // It seems we are in the middle of a multiline response
568
569 // Clear the error bits of the stream
570 In.clear();
571
572 // Extract the line
573 std::string Line;
574 std::getline(In, Line);
575
576 if (!Line.empty())
577 {
578 // Remove the ending '\r' (all lines are terminated by "\r\n")
579 Line.erase(Line.length() - 1);
580
581 // Append it to the current message
582 Message += Line + "\n";
583 }
584 }
585 else
586 {
587 // Error : cannot extract the code, and we are not in a multiline response
588 return Response(Response::InvalidResponse);
589 }
590 }
591 }
592
593 // We never reach there
594}
595
596
597////////////////////////////////////////////////////////////
598/// Constructor
599////////////////////////////////////////////////////////////
600Ftp::DataChannel::DataChannel(Ftp& Owner) :
601myFtp(Owner)
602{
603
604}
605
606
607////////////////////////////////////////////////////////////
608/// Destructor
609////////////////////////////////////////////////////////////
610Ftp::DataChannel::~DataChannel()
611{
612 // Close the data socket
613 myDataSocket.Close();
614}
615
616
617////////////////////////////////////////////////////////////
618/// Open the data channel using the specified mode and port
619////////////////////////////////////////////////////////////
620Ftp::Response Ftp::DataChannel::Open(Ftp::TransferMode Mode)
621{
622 // Open a data connection in active mode (we connect to the server)
623 Ftp::Response Resp = myFtp.SendCommand("PASV");
624 if (Resp.IsOk())
625 {
626 // Extract the connection address and port from the response
627 std::string::size_type begin = Resp.GetMessage().find_first_of("0123456789");
628 if (begin != std::string::npos)
629 {
630 sf::Uint8 Data[6] = {0, 0, 0, 0, 0, 0};
631 std::string Str = Resp.GetMessage().substr(begin);
632 std::size_t Index = 0;
633 for (int i = 0; i < 6; ++i)
634 {
635 // Extract the current number
636 while (isdigit(Str[Index]))
637 {
638 Data[i] = Data[i] * 10 + (Str[Index] - '0');
639 Index++;
640 }
641
642 // Skip separator
643 Index++;
644 }
645
646 // Reconstruct connection port and address
647 unsigned short Port = Data[4] * 256 + Data[5];
648 sf::IPAddress Address(static_cast<sf::Uint8>(Data[0]),
649 static_cast<sf::Uint8>(Data[1]),
650 static_cast<sf::Uint8>(Data[2]),
651 static_cast<sf::Uint8>(Data[3]));
652
653 // Connect the data channel to the server
654 if (myDataSocket.Connect(Port, Address) == Socket::Done)
655 {
656 // Translate the transfer mode to the corresponding FTP parameter
657 std::string ModeStr;
658 switch (Mode)
659 {
660 case Ftp::Binary : ModeStr = "I"; break;
661 case Ftp::Ascii : ModeStr = "A"; break;
662 case Ftp::Ebcdic : ModeStr = "E"; break;
663 }
664
665 // Set the transfer mode
666 Resp = myFtp.SendCommand("TYPE", ModeStr);
667 }
668 else
669 {
670 // Failed to connect to the server
671 Resp = Ftp::Response(Ftp::Response::ConnectionFailed);
672 }
673 }
674 }
675
676 return Resp;
677}
678
679
680////////////////////////////////////////////////////////////
681/// Receive data on the data channel until it is closed
682////////////////////////////////////////////////////////////
683void Ftp::DataChannel::Receive(std::vector<char>& Data)
684{
685 // Receive data
686 Data.clear();
687 char Buffer[1024];
688 std::size_t Received;
689 while (myDataSocket.Receive(Buffer, sizeof(Buffer), Received) == sf::Socket::Done)
690 {
691 std::copy(Buffer, Buffer + Received, std::back_inserter(Data));
692 }
693
694 // Close the data socket
695 myDataSocket.Close();
696}
697
698
699////////////////////////////////////////////////////////////
700/// Send data on the data channel
701////////////////////////////////////////////////////////////
702void Ftp::DataChannel::Send(const std::vector<char>& Data)
703{
704 // Send data
705 if (!Data.empty())
706 myDataSocket.Send(&Data[0], Data.size());
707
708 // Close the data socket
709 myDataSocket.Close();
710}
711
712} // namespace sf