How to write FTPClient in MobiAccess




Introduction:


The purpose of this document is to set an example for the users of the MobiAccess framework about how to write a FTPClient modul. The following description will guide through the reader step-by-step in the construction of the Modul, constantly adding new functions.
The first step

We need first, one helper class for represent the socket connection, and the FTPClient class:

FTPConnection

//Class for represent the socket connection class { //The ip address of the host public var ip : String;
//The connection port public var port : Integer;
public CI() { super(); } }

FTPClient

class { //The socket to connect ftpServer private var socket : Socket;
//The InputSream to receive basic information from the server private var sis : InputStream;
//The OutputStream to send commands to server private var sos : OutputStream;
//The server host private var host : String;
//The server connection port private var port : Integer;
//The error message private var errorMesssage : String;
//The newLine buffer private var newLine : ByteBuffer;
//The apostrof character private var apostrof : String;
//The 1KByte bufferSize private static var OneK : Long; public CI() { super();

this.newLine = ByteBuffer.CI(); this.newLine.AddByte(Byte.CI("13")); this.newLine.AddByte(Byte.CI("10"));var bb : ByteBuffer= ByteBuffer.CI(); bb.AddByte(Byte.CI("34")); this.apostrof = String.ParseBytes(bb); }
static { FTPClient.OneK = Long.CI("1024"); } }

Now we have one basic class to connect the FTP server

 

The Connection

We need one connection method:

//Connect to the remote host, return isSucces public function Connect(host : String, port : Integer) : Boolean;

Connect

Connect { var ex : Exception; try { this.socket = Socket.CI(host, port); this.sis = this.socket.GetInputStream(); this.sos = this.socket.GetOutputStream(); this.host = host; this.port = port; return true; } catch(ex) { this.errorMesssage = ex.GetMessage(); return false; } }

When the user connected, need login the server:



Login

For login the user we need some helper method to send command the server, and implement the login function.

 

ExecuteCommad

//Send command to server, end return the server response private function ExecuteCommand(command: String) : String;
ExecuteCommand { var bb : ByteBuffer = command.GetBytes(); bb.AddBytes(this.newLine); this.sos.WriteBytes(bb);

this.sos.Flush(); while(this.sis.Available().NOT()){} bb= ByteBuffer.CI(); while(this.sis.Available())

{ bb.AddBytes(this.sis.ReadBytes(FTPClient.OneK)); }
var result : String = String.ParseBytes(bb); return result; }

Login

//Login the user to the connected host, return isSuccess public function Login(userName : String, password : String) : Boolean;
Login { var ex: Exception; try { this.ExecuteCommand("");

var tmp : String = this.ExecuteCommand("USER ".Concatenate(userName)); if(tmp.Contains("331"))

{ tmp= this.ExecuteCommand("PASS ".Concatenate(password)); if(tmp.Contains("230"))

{ return true; } this.errorMesssage = tmp; } }

catch(ex) { this.errorMesssage = ex.GetMessage(); }
return false; }

Now, when user logged is, we can use some basic functions

 

Basic functions

PrintWorkingDirectory

//Returns the name of the current directory on the remote host. public function PrintWorkingDirectory() :String;
PrintWorkingDirectory { var ex : Exception; var tmp : String; try { tmp = this.ExecuteCommand("PWD");

if(tmp.Contains("257 ")) { var startIndex : Integer = tmp.IndexOf(this.apostrof);

tmp= tmp.SubString(startIndex.Add(1), tmp.Length().Subtract(startIndex.Add(1)));

var endIndex : Integer = tmp.IndexOf(this.apostrof); tmp = tmp.SubString(0,endIndex);

return tmp; } throw Exception.CI("Exception in PWD",tmp, null); } catch(ex)

{ this.errorMesssage = ex.GetMessage(); }
return null; }

ChangeDirectoryUp

//Makes the parent of the current directory be the current directory. public function ChangeDirectoryUp() : Boolean; ChangeDirectoryUp

{ var ex : Exception; var tmp : String ; try { tmp = this.ExecuteCommand("CDUP");

if(tmp.Contains("250")) { return true; } this.errorMesssage = tmp; }

catch(ex) { this.errorMesssage = ex.GetMessage(); } return false; }

ChangeWorkingDirectory

//Makes the given directory be the current directory on the remote host. public function ChangeWorkingDirectory(path : String) : Boolean;
ChangeWorkingDirectory { var tmp : String; var ex : Exception; try

{ tmp = this.ExecuteCommand("CWD ".Concatenate(path)); if(tmp.Contains("250"))

{ return true; } this.errorMesssage = tmp; } catch(ex)

{ this.errorMesssage = ex.GetMessage(); } return false; }

Delete

//Deletes the given file on the remote host. public function Delete(fileName : String) : Boolean;
Delete { var tmp : String; var ex : Exception; try { tmp = this.ExecuteCommand("DELE ".Concatenate(fileName));

if(tmp.Contains("250")) { return true; } this.errorMesssage = tmp; } catch(ex)

{ this.errorMesssage = ex.GetMessage(); } return false; }

MakeDirectory

//Creates the named directory on the remote host. public function MakeDirectory(dirName : String) : Boolean;
MakeDirectory { var tmp : String; var ex : Exception; try

{ tmp = this.ExecuteCommand("MKD ".Concatenate(dirName)); if(tmp.Contains("257"))

{ return true; } this.errorMesssage = tmp; } catch(ex)

{ this.errorMesssage = ex.GetMessage(); } return false; }

Quit

//Terminates the command connection. public function Quit() : Boolean;
Quit { var tmp : String; var ex : Exception; try { tmp = this.ExecuteCommand("QUIT"); if(tmp.Contains("221"))

{ return true; } this.errorMesssage = tmp; } catch(ex)

{ this.errorMesssage = ex.GetMessage(); } return false; }

GetFileSize

//Returns the size of the remote file as a decimal number. public function GetFileSize(fileName : String) : Long;
GetFileSize { var tmp : String; var ex : Exception; try { tmp = this.ExecuteCommand("SIZE ".Concatenate(fileName));

 if(tmp.StartsWith("213 ")) { tmp = tmp.Replace("213 ", "");

tmp = tmp.Replace(String.ParseBytes(this.newLine), ""); return Long.CI(tmp); }

this.errorMesssage = tmp; } catch(ex) { this.errorMesssage = ex.GetMessage(); }

return null; }

RemoveDirectory

//Deletes the named directory on the remote host. public function RemoveDirectory(dirName : String) : Boolean;
RemoveDirectory { var tmp : String; var ex : Exception; try

{ tmp = this.ExecuteCommand("RMD ".Concatenate(dirName)); if(tmp.StartsWith("250"))

return true; } this.errorMesssage = tmp; } catch(ex)

{ this.errorMesssage = ex.GetMessage(); } return false; }

RenameFile

//Rename the given file public function RenameFile(oldName : String, newName : String) : Boolean;
RenameFile { var tmp : String; var ex : Exception; try { tmp = this.ExecuteCommand("RNFR ".Concatenate(oldName));

if(tmp.StartsWith("350")) { tmp = this.ExecuteCommand("RNTO ".Concatenate(newName));

if(tmp.StartsWith("250")) { return true; } } }

catch(ex) { this.errorMesssage = ex.GetMessage(); } return false; }

 

DataTransfer functions

For send and receive files, we need implement some helper methods:

 

Helper methods:

InitPassiveConnection

//Init the passive connection for the data transfer private function InitPassiveConnection() : Void;
InitPassiveConnection { var connection : FTPConnection = this.PassiveMode(); this.ClosePassiveSocket();

this.passiveSocket = Socket.CI(connection.ip, connection.port); this.passiveSocket.SetReadTimeOut(Long.CI("5000"));

this.passiveSIS= this.passiveSocket.GetInputStream(); this.passiveSOS = this.passiveSocket.GetOutputStream(); return; }


SetType

//Set the transfer type private function SetType(type : String) : Boolean;
SetType { var tmp : String; var ex : Exception; try { tmp = this.ExecuteCommand("TYPE ".Concatenate(type));

if(tmp.Contains("200")) { return true; } this.errorMesssage = tmp; } catch(ex)

{ this.errorMesssage = ex.GetMessage(); } return false; }

ReadDataFromStream

//Read all data from the given stream, and return the ByteBuffer private function ReadDataFromStream(is : InputStream) : ByteBuffer;
ReadDataFromStream { var bb : ByteBuffer = ByteBuffer.CI(); while(is.Available().NOT()) { } while(is.Available())

{ bb.AddBytes(is.ReadBytes(FTPClient.OneK)); } Log.PrintLine(String.ParseBytes(bb)); return bb; }

PassiveMode

//Send server passiveMode request, and return the connection info private function PassiveMode() :FTPConnection;
PassiveMode { var tmp : String; var ex : Exception; try { tmp= this.ExecuteCommand("PASV");

if(tmp.Contains("227")) { var connection : FTPConnection = FTPConnection.CI();

var beginIndex : Integer = tmp.IndexOf("("); var endIndex : Integer = tmp.IndexOf(")");

tmp= tmp.SubString(beginIndex.Add(1), endIndex.Subtract(beginIndex).Subtract(1));

tmp = tmp.Replace(" ", ""); var list : List = tmp.Split(",");
connection.ip = ([String]list.GetItem(0)).Concatenate(".").Concatenate(([String]list.GetItem(1)))

.Concatenate(".").Concatenate(([String]list.GetItem(2))).Concatenate(".").Concatenate(([String]list.GetItem(3)));

connection.port = Integer.CI([String]list.GetItem(4)).Multiply(256).Add(Integer.CI([String]list.GetItem(5)));

//this.isPassiveMode = true; return connection; } this.errorMesssage = tmp; }

catch(ex) { this.errorMesssage = ex.GetMessage(); //throw ex; } return null; }

ClosePassiveSocket

//Close the dataTransfer socket private function ClosePassiveSocket() : Void;
ClosePassiveSocket { var ex : Exception; try { this.passiveSIS.Close(); } catch(ex){} try

{ this.passiveSOS.Close(); } catch(ex){} try { this.passiveSocket.Close(); }

catch(ex){} return; }

TransferMethods

GetFile

//Receive the given file from the server and write it outputStream public function GetFile(fileName : String, fos : FileOutputStream) : Boolean;
GetFile { var tmp : String; var ex : Exception; var bb : ByteBuffer; var size : Long;

var bufferSize : Long = Long.CI("131072"); var readCounter : Long = Long.CI("0"); try

{ size = this.GetFileSize(fileName); if(this.SetType("I").NOT())

{ return false; } this.InitPassiveConnection();

tmp = this.ExecuteCommand("RETR ".Concatenate(fileName)); if(tmp.Contains("150"))

{ while(readCounter.Smaller(size)) { bb= this.passiveSIS.ReadBytes(bufferSize);

readCounter = readCounter.Add(bb.GetLength().ToLong()); fos.WriteBytes(bb); fos.Flush(); }

tmp = String.ParseBytes(this.ReadDataFromStream(this.sis)); if(tmp.StartsWith("226"))

{ return true; } } this.errorMesssage = tmp; } catch(ex)

{ this.errorMesssage = ex.GetMessage(); } return false; }

PushFile

//Push file to the server public function PushFile(fileName : String, fis : InputStream) : Boolean;
PushFile { var tmp : String; var ex : Exception; var bb : ByteBuffer;
var bufferSize : Long = Long.CI("131072"); try { this.InitPassiveConnection();

tmp = this.ExecuteCommand("STOR ".Concatenate(fileName)); if(tmp.StartsWith("150"))

{ while(fis.Available()) { bb = fis.ReadBytes(bufferSize);

this.passiveSOS.WriteBytes(bb); this.passiveSOS.Flush(); }

this.passiveSOS.Close(); tmp = String.ParseBytes(this.ReadDataFromStream(this.sis));

if(tmp.StartsWith("226")) { return true; } }

this.errorMesssage = tmp; catch(ex) { this.errorMesssage = ex.GetMessage(); } return false; }

Written by SJ

Add comment


Security code
Refresh