1 /* 2 DSFML - The Simple and Fast Multimedia Library for D 3 4 Copyright (c) 2013 - 2015 Jeremy DeHaan (dehaan.jeremiah@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, including commercial applications, 10 and to alter it and redistribute it freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 14 15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 16 17 3. This notice may not be removed or altered from any source distribution 18 */ 19 20 ///A module containing the Ftp class. 21 module dsfml.network.ftp; 22 23 import core.time; 24 25 import dsfml.network.ipaddress; 26 27 28 /** 29 *A FTP client. 30 * 31 *The Ftp class is a very simple FTP client that allows you to communicate with a FTP server. 32 * 33 *The FTP protocol allows you to manipulate a remote file system (list files, upload, download, create, remove, ...). 34 */ 35 class Ftp 36 { 37 ///Enumeration of transfer modes. 38 enum TransferMode 39 { 40 ///Binary mode (file is transfered as a sequence of bytes) 41 Binary, 42 ///Text mode using ASCII encoding. 43 Ascii, 44 ///Text mode using EBCDIC encoding. 45 Ebcdic, 46 } 47 48 package sfFtp* sfPtr; 49 50 ///Default Constructor. 51 this() 52 { 53 sfPtr = sfFtp_create(); 54 } 55 56 ///Destructor 57 ~this() 58 { 59 import dsfml.system.config; 60 mixin(destructorOutput); 61 sfFtp_destroy(sfPtr); 62 } 63 64 65 ///Get the current working directory. 66 /// 67 ///The working directory is the root path for subsequent operations involving directories and/or filenames. 68 /// 69 ///Returns: Server response to the request. 70 DirectoryResponse getWorkingDirectory() 71 { 72 return new DirectoryResponse(sfFtp_getWorkingDirectory(sfPtr)); 73 } 74 75 ///Get the contents of the given directory. 76 /// 77 ///This function retrieves the sub-directories and files contained in the given directory. It is not recursive. The directory parameter is relative to the current working directory. 78 /// 79 ///Returns: Server response to the request. 80 ListingResponse getDirectoryListing(string directory = "") 81 { 82 import dsfml.system..string; 83 return new ListingResponse(sfFtp_getDirectoryListing(sfPtr, toStringz(directory))); 84 } 85 ///Change the current working directory. 86 /// 87 ///The new directory must be relative to the current one. 88 /// 89 ///Returns: Server response to the request. 90 Response changeDirectory(string directory) 91 { 92 import dsfml.system..string; 93 return new Response(sfFtp_changeDirectory(sfPtr,toStringz(directory))); 94 } 95 96 ///Connect to the specified FTP server. 97 /// 98 ///The port has a default value of 21, which is the standard port used by the FTP protocol. You shouldn't use a different value, unless you really know what you do. 99 ///This function tries to connect to the server so it may take a while to complete, especially if the server is not reachable. To avoid blocking your application for too long, you can use a timeout. The default value, Time::Zero, means that the system timeout will be used (which is usually pretty long). 100 /// 101 ///Params: 102 /// address = Address of the FTP server to connect to. 103 /// port = Port used for the connection. 104 /// timeout = Maximum time to wait. 105 /// 106 ///Returns: Server response to the request. 107 Response connect(IpAddress address, ushort port = 21, Duration timeout = Duration.zero()) 108 { 109 return new Response(sfFtp_connect(sfPtr, address.m_address.ptr, port, timeout.total!"usecs")); 110 } 111 112 ///Connect to the specified FTP server. 113 /// 114 ///The port has a default value of 21, which is the standard port used by the FTP protocol. You shouldn't use a different value, unless you really know what you do. 115 ///This function tries to connect to the server so it may take a while to complete, especially if the server is not reachable. To avoid blocking your application for too long, you can use a timeout. The default value, Time::Zero, means that the system timeout will be used (which is usually pretty long). 116 /// 117 ///Params: 118 /// address = Name or ddress of the FTP server to connect to. 119 /// port = Port used for the connection. 120 /// timeout = Maximum time to wait. 121 /// 122 ///Returns: Server response to the request. 123 Response connect(string address, ushort port = 21, Duration timeout = Duration.zero()) 124 { 125 return new Response(sfFtp_connect(sfPtr, IpAddress(address).m_address.ptr, port, timeout.total!"usecs")); 126 } 127 128 ///Remove an existing directory. 129 /// 130 ///The directory to remove must be relative to the current working directory. Use this function with caution, the directory will be removed permanently! 131 /// 132 ///Params: 133 /// name = Name of the directory to remove. 134 /// 135 ///Returns: Server response to the request. 136 Response deleteDirectory(string name) 137 { 138 import dsfml.system..string; 139 return new Response(sfFtp_deleteDirectory(sfPtr, toStringz(name))); 140 } 141 142 ///Remove an existing file. 143 /// 144 ///The file name must be relative to the current working directory. Use this function with caution, the file will be removed permanently! 145 /// 146 ///Params: 147 /// name = Name of the file to remove. 148 /// 149 ///Returns: Server response to the request. 150 Response deleteFile(string name) 151 { 152 import dsfml.system..string; 153 return new Response(sfFtp_deleteFile(sfPtr, toStringz(name))); 154 } 155 156 ///Close the connection with the server. 157 /// 158 ///Returns: Server response to the request. 159 Response disconnect() 160 { 161 import dsfml.system..string; 162 return new Response(sfFtp_disconnect(sfPtr)); 163 } 164 165 ///Download a file from the server. 166 /// 167 ///The filename of the distant file is relative to the current working directory of the server, and the local destination path is relative to the current directory of your application. 168 /// 169 ///Params: 170 /// remoteFile = Filename of the distant file to download. 171 /// localPath = Where to put to file on the local computer. 172 /// mode = Transfer mode. 173 /// 174 ///Returns: Server response to the request. 175 Response download(string remoteFile, string localPath, TransferMode mode = TransferMode.Binary) 176 { 177 import dsfml.system..string; 178 return new Response(sfFtp_download(sfPtr, toStringz(remoteFile),toStringz(localPath),mode)); 179 } 180 181 ///Send a null command to keep the connection alive. 182 /// 183 ///This command is useful because the server may close the connection automatically if no command is sent. 184 /// 185 ///Returns: Server response to the request. 186 Response keepAlive() 187 { 188 return new Response(sfFtp_keepAlive(sfPtr)); 189 } 190 191 ///Log in using an anonymous account. 192 /// 193 ///Logging in is mandatory after connecting to the server. Users that are not logged in cannot perform any operation. 194 /// 195 ///Returns: Server response to the request. 196 Response login() 197 { 198 return new Response(sfFtp_loginAnonymous(sfPtr)); 199 } 200 201 ///Log in using a username and a password. 202 /// 203 ///Logging in is mandatory after connecting to the server. Users that are not logged in cannot perform any operation. 204 /// 205 ///Params: 206 /// name = User name. 207 /// password = The password. 208 /// 209 ///Returns: Server response to the request. 210 Response login(string name, string password) 211 { 212 import dsfml.system..string; 213 return new Response(sfFtp_login(sfPtr,toStringz(name), toStringz(password))); 214 } 215 216 ///Go to the parent directory of the current one. 217 /// 218 ///Returns: Server response to the request. 219 Response parentDirectory() 220 { 221 import dsfml.system..string; 222 return new Response(sfFtp_parentDirectory(sfPtr)); 223 } 224 225 ///Create a new directory. 226 /// 227 ///The new directory is created as a child of the current working directory. 228 /// 229 ///Params: 230 /// name = Name of the directory to create. 231 /// 232 ///Returns: Server response to the request. 233 Response createDirectory(string name) 234 { 235 import dsfml.system..string; 236 return new Response(sfFtp_createDirectory(sfPtr, toStringz(name))); 237 } 238 239 ///Rename an existing file. 240 /// 241 ///The filenames must be relative to the current working directory. 242 /// 243 ///Params: 244 /// file = File to rename. 245 /// newName = New name of the file. 246 /// 247 ///Returns: Server response to the request. 248 Response renameFile(string file, string newName) 249 { 250 import dsfml.system..string; 251 return new Response(sfFtp_renameFile(sfPtr,toStringz(file),toStringz(newName))); 252 } 253 254 ///Upload a file to the server. 255 /// 256 ///The name of the local file is relative to the current working directory of your application, and the remote path is relative to the current directory of the FTP server. 257 /// 258 ///Params: 259 /// localFile = Path of the local file to upload. 260 /// remotePath = Where to put the file on the server. 261 /// mode = Transfer mode. 262 /// 263 ///Returns: Server response to the request. 264 Response upload(string localFile, string remotePath, TransferMode mode = TransferMode.Binary) 265 { 266 import dsfml.system..string; 267 return new Response(sfFtp_upload(sfPtr,toStringz(localFile),toStringz(remotePath),mode)); 268 } 269 270 ///Specialization of FTP response returning a directory. 271 class DirectoryResponse:Response 272 { 273 private string Directory; 274 275 //Internally used constructor 276 package this(sfFtpDirectoryResponse* FtpDirectoryResponce) 277 { 278 import dsfml.system..string; 279 280 Directory = dsfml.system..string.toString(sfFtpDirectoryResponse_getDirectory(FtpDirectoryResponce)); 281 282 super(sfFtpDirectoryResponse_getStatus(FtpDirectoryResponce), sfFtpDirectoryResponse_getMessage(FtpDirectoryResponce)); 283 284 sfFtpDirectoryResponse_destroy(FtpDirectoryResponce); 285 } 286 287 ///Get the directory returned in the response. 288 /// 289 ///Returns: Directory name. 290 string getDirectory() 291 { 292 return Directory; 293 } 294 } 295 296 ///Specialization of FTP response returning a filename lisiting. 297 class ListingResponse:Response 298 { 299 private string[] Filenames; 300 301 //Internally used constructor 302 package this(sfFtpListingResponse* FtpListingResponce) 303 { 304 import dsfml.system..string; 305 306 Filenames.length = sfFtpListingResponse_getCount(FtpListingResponce); 307 for(int i = 0; i < Filenames.length; i++) 308 { 309 Filenames[i] = dsfml.system..string.toString(sfFtpListingResponse_getName(FtpListingResponce,i)); 310 } 311 312 super(sfFtpListingResponse_getStatus(FtpListingResponce), sfFtpListingResponse_getMessage(FtpListingResponce)); 313 314 sfFtpListingResponse_destroy(FtpListingResponce); 315 316 } 317 318 ///Return the array of directory/file names. 319 /// 320 ///Returns: Array containing the requested listing. 321 const(string[]) getFilenames() 322 { 323 return Filenames; 324 } 325 } 326 327 ///Define a FTP response. 328 class Response 329 { 330 ///Status codes possibly returned by a FTP response. 331 enum Status 332 { 333 RestartMarkerReply = 110, 334 ServiceReadySoon = 120, 335 DataConnectionAlreadyOpened = 125, 336 OpeningDataConnection = 150, 337 338 Ok = 200, 339 PointlessCommand = 202, 340 SystemStatus = 211, 341 DirectoryStatus = 212, 342 FileStatus = 213, 343 HelpMessage = 214, 344 SystemType = 215, 345 ServiceReady = 220, 346 ClosingConnection = 221, 347 DataConnectionOpened = 225, 348 ClosingDataConnection = 226, 349 EnteringPassiveMode = 227, 350 LoggedIn = 230, 351 FileActionOk = 250, 352 DirectoryOk = 257, 353 354 NeedPassword = 331, 355 NeedAccountToLogIn = 332, 356 NeedInformation = 350, 357 ServiceUnavailable = 421, 358 DataConnectionUnavailable = 425, 359 TransferAborted = 426, 360 FileActionAborted = 450, 361 LocalError = 451, 362 InsufficientStorageSpace = 452, 363 364 CommandUnknown = 500, 365 ParametersUnknown = 501, 366 CommandNotImplemented = 502, 367 BadCommandSequence = 503, 368 ParameterNotImplemented = 504, 369 NotLoggedIn = 530, 370 NeedAccountToStore = 532, 371 FileUnavailable = 550, 372 PageTypeUnknown = 551, 373 NotEnoughMemory = 552, 374 FilenameNotAllowed = 553, 375 376 InvalidResponse = 1000, 377 ConnectionFailed = 1001, 378 ConnectionClosed = 1002, 379 InvalidFile = 1003, 380 } 381 382 private Status FtpStatus; 383 private string Message; 384 385 //Internally used constructor. 386 package this(sfFtpResponse* FtpResponce) 387 { 388 this(sfFtpResponse_getStatus(FtpResponce),sfFtpResponse_getMessage(FtpResponce)); 389 sfFtpResponse_destroy(FtpResponce); 390 } 391 392 //Internally used constructor. 393 package this(Ftp.Response.Status status = Ftp.Response.Status.InvalidResponse, const(char)* message = "") 394 { 395 import dsfml.system..string; 396 FtpStatus = status; 397 Message = dsfml.system..string.toString(message); 398 } 399 400 ///Get the full message contained in the response. 401 /// 402 ///Returns: The response message. 403 string getMessage() const 404 { 405 return Message; 406 } 407 408 ///Get the status code of the response. 409 /// 410 ///Returns: Status code. 411 Status getStatus() const 412 { 413 return FtpStatus; 414 } 415 416 ///Check if the status code means a success. 417 /// 418 ///This function is defined for convenience, it is equivalent to testing if the status code is < 400. 419 /// 420 ///Returns: True if the status is a success, false if it is a failure. 421 bool isOk() const 422 { 423 return FtpStatus< 400; 424 } 425 } 426 } 427 unittest 428 { 429 version(DSFML_Unittest_Network) 430 { 431 import std.stdio; 432 import dsfml.system.err; 433 434 writeln("Unittest for Ftp"); 435 436 auto ftp = new Ftp(); 437 438 auto responce = ftp.connect("ftp.microsoft.com"); 439 440 if(responce.isOk()) 441 { 442 writeln("Connected! Huzzah!"); 443 } 444 else 445 { 446 writeln("Uh-oh"); 447 writeln(responce.getStatus()); 448 assert(0); 449 } 450 451 //annonymous log in 452 responce = ftp.login(); 453 if(responce.isOk()) 454 { 455 writeln("Logged in! Huzzah!"); 456 } 457 else 458 { 459 writeln("Uh-oh"); 460 writeln(responce.getStatus()); 461 assert(0); 462 } 463 464 465 auto directory = ftp.getWorkingDirectory(); 466 if (directory.isOk()) 467 { 468 writeln("Working directory: ", directory.getDirectory()); 469 } 470 471 auto listing = ftp.getDirectoryListing(); 472 473 if(listing.isOk()) 474 { 475 const(string[]) list = listing.getFilenames(); 476 477 size_t length; 478 479 if(list.length > 10) 480 { 481 length = 10; 482 } 483 else 484 { 485 length = list.length; 486 } 487 488 for(int i= 0; i < length; ++i) 489 { 490 writeln(list[i]); 491 } 492 } 493 494 writeln(); 495 } 496 } 497 498 private extern(C): 499 500 501 struct sfFtpDirectoryResponse; 502 struct sfFtpListingResponse; 503 struct sfFtpResponse; 504 struct sfFtp; 505 506 //FTP Listing Response Functions 507 508 ///Destroy a FTP listing response 509 void sfFtpListingResponse_destroy(sfFtpListingResponse* ftpListingResponse); 510 511 512 ///Get the status code of a FTP listing response 513 Ftp.Response.Status sfFtpListingResponse_getStatus(const sfFtpListingResponse* ftpListingResponse); 514 515 516 ///Get the full message contained in a FTP listing response 517 const(char)* sfFtpListingResponse_getMessage(const(sfFtpListingResponse)* ftpListingResponse); 518 519 520 ///Return the number of directory/file names contained in a FTP listing response 521 size_t sfFtpListingResponse_getCount(const(sfFtpListingResponse)* ftpListingResponse); 522 523 524 ///Return a directory/file name contained in a FTP listing response 525 const(char)* sfFtpListingResponse_getName(const(sfFtpListingResponse)* ftpListingResponse, size_t index); 526 527 528 529 //FTP Directory Responce Functions 530 531 ///Destroy a FTP directory response 532 void sfFtpDirectoryResponse_destroy(sfFtpDirectoryResponse* ftpDirectoryResponse); 533 534 535 ///Get the status code of a FTP directory response 536 Ftp.Response.Status sfFtpDirectoryResponse_getStatus(const(sfFtpDirectoryResponse)* ftpDirectoryResponse); 537 538 539 ///Get the full message contained in a FTP directory response 540 const(char)* sfFtpDirectoryResponse_getMessage(const(sfFtpDirectoryResponse)* ftpDirectoryResponse); 541 542 543 ///Get the directory returned in a FTP directory response 544 const(char)* sfFtpDirectoryResponse_getDirectory(const(sfFtpDirectoryResponse)* ftpDirectoryResponse); 545 546 547 548 //FTP Responce functions 549 550 ///Destroy a FTP response 551 void sfFtpResponse_destroy(sfFtpResponse* ftpResponse); 552 553 554 ///Get the status code of a FTP response 555 Ftp.Response.Status sfFtpResponse_getStatus(const(sfFtpResponse)* ftpResponse); 556 557 558 ///Get the full message contained in a FTP response 559 const (char)* sfFtpResponse_getMessage(const sfFtpResponse* ftpResponse); 560 561 562 ////FTP functions 563 564 ///Create a new Ftp object 565 sfFtp* sfFtp_create(); 566 567 568 ///Destroy a Ftp object 569 void sfFtp_destroy(sfFtp* ftp); 570 571 572 ///Connect to the specified FTP server 573 sfFtpResponse* sfFtp_connect(sfFtp* ftp, const(char)* serverIP, ushort port, long timeout); 574 575 576 ///Log in using an anonymous account 577 sfFtpResponse* sfFtp_loginAnonymous(sfFtp* ftp); 578 579 580 ///Log in using a username and a password 581 sfFtpResponse* sfFtp_login(sfFtp* ftp, const(char)* userName, const(char)* password); 582 583 584 ///Close the connection with the server 585 sfFtpResponse* sfFtp_disconnect(sfFtp* ftp); 586 587 588 ///Send a null command to keep the connection alive 589 sfFtpResponse* sfFtp_keepAlive(sfFtp* ftp); 590 591 592 ///Get the current working directory 593 sfFtpDirectoryResponse* sfFtp_getWorkingDirectory(sfFtp* ftp); 594 595 596 ///Get the contents of the given directory 597 sfFtpListingResponse* sfFtp_getDirectoryListing(sfFtp* ftp, const(char)* directory); 598 599 600 ///Change the current working directory 601 sfFtpResponse* sfFtp_changeDirectory(sfFtp* ftp, const(char)* directory); 602 603 604 ///Go to the parent directory of the current one 605 sfFtpResponse* sfFtp_parentDirectory(sfFtp* ftp); 606 607 608 ///Create a new directory 609 sfFtpResponse* sfFtp_createDirectory(sfFtp* ftp, const(char)* name); 610 611 612 ///Remove an existing directory 613 sfFtpResponse* sfFtp_deleteDirectory(sfFtp* ftp, const(char)* name); 614 615 616 ///Rename an existing file 617 sfFtpResponse* sfFtp_renameFile(sfFtp* ftp, const(char)* file, const(char)* newName); 618 619 620 ///Remove an existing file 621 sfFtpResponse* sfFtp_deleteFile(sfFtp* ftp, const(char)* name); 622 623 624 ///Download a file from a FTP server 625 sfFtpResponse* sfFtp_download(sfFtp* ftp, const(char)* distantFile, const(char)* destPath, int mode); 626 627 628 ///Upload a file to a FTP server 629 sfFtpResponse* sfFtp_upload(sfFtp* ftp, const(char)* localFile, const(char)* destPath, int mode);