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