Sign in to follow this  
Followers 0

UDF to support SFTP protocol using PSFTP

31 posts in this topic

Posted (edited)

I started to work on this UDF and I'd like to discuss, test and improve it with other interested users.

The idea is to create a UDF similar to FTPEx, but to support SFTP protocol. Given that this protocol is not natively supported by Windows, I found some candidates to manage it:
cURL: http://curl.haxx.se/
PSFTP: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
FZSFTP: http://filezilla-project.org/

I'm trying to use the second one (realied by PuTTY team) and the result is the following code. It is not complete and needs to be widely tested, but main functions work and could be considered as a draft to start the discussion.

A possible alternative could be to use FZSFTP (a modified version of PSFTP realized for FileZilla), that includes some interesting improvements to be used by external programs, but does not offer a related documentation.


Code version: 1.0 beta 9 (not complete)
Previous downloads: 132

SFTPEx.au3

Edited by Lupo73
MuffettsMan, Neutro and GreenCan like this

Share this post


Link to post
Share on other sites



Posted

Nice work. If you want to avoid using an additional process, I know of two possible DLLs you can use: libcurl and libssh2.

Share this post


Link to post
Share on other sites

Posted

They could be other good alternatives, but I never worked with DLLs before and I may need few help to start. Do you think use a dll is a better solution?

My idea is to start the discussion about it to decide what solution follow. I think could be interesting for several users to create a stable UDF for SFTP support (and maybe also for other protocols like FTPS using SSL). I don't know what are the limits and rules to create official UDFs, but with the collaboration of some users I'd like to create a good library to extend FTP support to other protocols.

Share this post


Link to post
Share on other sites

Posted

Code updated: minor fixes and standardized variable names (version 1.0 beta).

Share this post


Link to post
Share on other sites

Posted

Code updated: more improvements and started to add descriptions as a standard UDF (version 1.0 beta 2).

Share this post


Link to post
Share on other sites

Posted (edited)

Code updated: added _SFTP_FileMove() and _SFTP_ListToArray() functions, improved _SFTP_ListToArrayEx() and fixed some issues (version 1.0 beta 3).

Edited by Lupo73

Share this post


Link to post
Share on other sites

Posted

Code updated: completed _SFTP_ListToArray() and _SFTP_ListToArrayEx(), planned to add _SFTP_FileExists() and _SFTP_FileGetSize(), fixed other issues (version 1.0 beta 4).

Share this post


Link to post
Share on other sites

Posted

Code updated: added _SFTP_FileExists() and _SFTP_FileGetSize(), partially added _SFTP_ProgressDownload(), fixed other minor issues (version 1.0 beta 5).

ps: something like _SFTP_FileExists() could be easily added also to FTPEx library, what do you think about it?

Share this post


Link to post
Share on other sites

Posted

Code updated: improved _SFTP_ListToArray() and _SFTP_ListToArrayEx(), added _SFTP_DirDelete(), fixed _SFTP_DirGetContents() and _SFTP_FileGet(), fixed other minor issues (version 1.0 beta 6).

Share this post


Link to post
Share on other sites

Posted (edited)

Code updated: completed most of the functions (tests are welcome!)

Known limits: _SFTP_ListToArrayEx() loads time based on server timezone (not converted to the local timezone), _SFTP_ProgressDownload() doesn't correctly update progress bar downloading folders

I created also 3 functions that could be added to FTPEx:

; #FUNCTION# ====================================================================================================================
; Name...........: _FTP_FileExists
; Description ...: Checks if a file or folder exists on a FTP server.
; Syntax.........: _FTP_FileExists ( $hSession, $sRemoteFile )
; Parameters ....: $hSession - as returned by _FTP_Connect().
;				  $sRemoteFile - The remote file.
; Return values .: Success - 1
;				  Failure - 0, sets @error
;				  |1 - The connection is closed
;				  |2 - Directory not found
;				  |3 - Directory is empty
; Author ........: Lupo73
; Modified.......:
; Remarks .......:
; Related .......: _FTP_Connect
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _FTP_FileExists($hSession, $sRemoteFile)
    If $hSession = 0 Then
        Return SetError(1, 0, 0)
    EndIf

    Local $aStringSplit, $aFileList, $sPreviousDir, $iExists = 0, $sRemoteDir = ""
    $aStringSplit = StringSplit($sRemoteFile, "/")
    If $aStringSplit[0] > 1 Then
        $sRemoteDir = StringTrimRight($sRemoteFile, StringLen($aStringSplit[$aStringSplit[0]]) + 1)
        $sRemoteFile = $aStringSplit[$aStringSplit[0]]
        $sPreviousDir = _FTP_DirGetCurrent($hSession)
        _FTP_DirSetCurrent($hSession, $sRemoteDir)
    EndIf
    $aFileList = _FTP_ListToArray($hSession)
    If @error Then
        _FTP_DirSetCurrent($hSession, $sPreviousDir)
        Return SetError(2, 0, 0)
    EndIf
    _FTP_DirSetCurrent($hSession, $sPreviousDir)
    If $aFileList[0] = 0 Then
        Return SetError(3, 0, 0)
    EndIf

    For $A = 1 To $aFileList[0]
        If $sRemoteFile = $aFileList[$A] Then
            $iExists = 1
            ExitLoop
        EndIf
    Next

    Return $iExists
EndFunc   ; ==>_FTP_FileExists

; #FUNCTION# ====================================================================================================================
; Name...........: _FTP_FileGetInfo
; Description ...: Get info of a file or folder from a FTP server.
; Syntax.........: _FTP_FileGetInfo ( $hSession, $sRemoteFile )
; Parameters ....: $hSession - as returned by _FTP_Connect().
;				  $sRemoteFile - The remote file.
; Return values .: Success - returns an Array containing file info.
;				  Failure - 0, sets @error
;				  |1 - The connection is closed
;				  |2 - Directory not found
;				  |3 - Directory is empty
;				  |4 - File not found
; Author ........: Lupo73
; Modified.......:
; Remarks .......: Array[0] Filename
;				  Array[1] Filesize
;				  Array[2] Permissions
;				  Array[3] File Modification datetime
;				  Array[4] File Creation datetime
;				  Array[5] File Access datetime
; Related .......: _FTP_Connect, _FTP_ListToArrayEx
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _FTP_FileGetInfo($hSession, $sRemoteFile)
    If $hSession = 0 Then
        Return SetError(1, 0, 0)
    EndIf

    Local $aArray[6], $aStringSplit, $aFileList, $sPreviousDir, $sRemoteDir = ""
    $aStringSplit = StringSplit($sRemoteFile, "/")
    If $aStringSplit[0] > 1 Then
        $sRemoteDir = StringTrimRight($sRemoteFile, StringLen($aStringSplit[$aStringSplit[0]]) + 1)
        $sRemoteFile = $aStringSplit[$aStringSplit[0]]
        $sPreviousDir = _FTP_DirGetCurrent($hSession)
        _FTP_DirSetCurrent($hSession, $sRemoteDir)
    EndIf
    $aFileList = _FTP_ListToArrayEx($hSession)
    If @error Then
        _FTP_DirSetCurrent($hSession, $sPreviousDir)
        Return SetError(2, 0, 0)
    EndIf
    _FTP_DirSetCurrent($hSession, $sPreviousDir)
    If $aFileList[0][0] = 0 Then
        Return SetError(3, 0, 0)
    EndIf

    For $A = 1 To $aFileList[0][0]
        If $sRemoteFile = $aFileList[$A][0] Then
            For $B = 0 To 5
                $aArray[$B] = $aFileList[$A][$B]
            Next
            Return $aArray
        EndIf
    Next

    Return SetError(4, 0, 0)
EndFunc   ; ==>_FTP_FileGetInfo

; #FUNCTION# ====================================================================================================================
; Name...........: __FTP_ListToArrayEx2
; Description ...: Get names, sizes, attributes and times of files/folders into defined remote directory.
; Syntax.........: __FTP_ListToArrayEx2 ( $hSession [, $sRemoteDir = "" [, $ReturnType = 0 [, $iFlags = 0 [, $fTimeFormat = 1]]]] )
; Parameters ....: $hSession - as returned by _FTP_Connect().
;				  $sRemoteDir - Optional, The remote Directory (or use current remote Directory if not defined).
;				  $ReturnType - Optional, 0 = Both Files and Folders (default), 1 = Folders, 2 = Files.
;				  $iFlags - Optional, see _FTP_FindFileFirst().
;				  $fTimeFormat - Optional, type on the date strings: 1 = YYYY/MM/DD[ HH:MM] (default), 0 = MM/DD/YYYY[ HH:MM].
; Return values .: Success - returns an Array containing file info.
;				  Failure - 0, sets @error same of _FTP_ListToArrayEx()
; Author ........: Lupo73
; Modified.......:
; Remarks .......: Array[0][0] = Number of found entries
;				  Array[x][0] Filename
;				  Array[x][1] Filesize
;				  Array[x][2] Permissions
;				  Array[x][3] File Modification datetime
;				  Array[x][4] File Creation datetime
;				  Array[x][5] File Access datetime
; Related .......: _FTP_Connect, _FTP_ListToArrayEx, _FTP_FindFileFirst
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func __FTP_ListToArrayEx2($hSession, $sRemoteDir = "", $ReturnType = 0, $iFlags = 0, $fTimeFormat = 1)
    If $hSession = 0 Then
        Return SetError(1, 0, 0)
    EndIf

    Local $aFileList, $sPreviousDir, $iError = 0
    $sPreviousDir = _FTP_DirGetCurrent($hSession)
    _FTP_DirSetCurrent($hSession, $sRemoteDir)
    $aFileList = _FTP_ListToArrayEx($hSession, $ReturnType, $iFlags, $fTimeFormat)
    $iError = @error
    _FTP_DirSetCurrent($hSession, $sPreviousDir)
    If $iError Then
        Return SetError($iError, 0, $aFileList)
    EndIf

    Return $aFileList
EndFunc   ; ==>__FTP_ListToArrayEx2
Edited by Lupo73

Share this post


Link to post
Share on other sites

Posted

Cool UDF. Saves me time from tinkering with psftp :thumbsup:

However you may also want to add additional logic on this particular scenario below for first time connection and have it default to either yes or no:

The server's host key is not cached in the registry. You

have no guarantee that the server is the computer you

think it is.

The server's rsa2 key fingerprint is:

ssh-rsa 1024 ***********************************

If you trust this host, enter "y" to add the key to

PuTTY's cache and carry on connecting.

If you want to carry on connecting just once, without

adding the key to the cache, enter "n".

If you do not trust this host, press Return to abandon the

connection.

Store key in cache? (y/n)

Thanks :P

Share this post


Link to post
Share on other sites

Posted

It turns out that the window is still accepting input. It was the stdout that did not get updated. When I inserted some StdinWrite($hSession, @CRLF) on line 160 the window responded with "Too many logon attempts.". This is right after I inserted StdinWrite($hSession, "y"'& @CRLF) on line 140 after the Sleep(10). Login also worked when I did the same thing with the credentials.

Share this post


Link to post
Share on other sites

Posted

This looks great, but when I try to use it, I get an error. SciTE points at line 1116, in the _SFTP_Open function. Specifically, this line.

Local $hSession = Run($sPath, "", @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD)

It looks like $STDIN_CHILD and $STDOUT_CHILD aren't declared anywhere else in the script, and aren't passed as input. Can you point me in the right direction as to what those should be doing?

Share this post


Link to post
Share on other sites

Posted

Check the help file, these can be found in the Constants.au3 file, just include it in your script.

Share this post


Link to post
Share on other sites

Posted

Thank you Guinness, that worked. And thank you Lupo73 for the UDF in the first place!

Share this post


Link to post
Share on other sites

Posted

Minor suggested alteration to the code. At the moment, _SFTP_Close() does not log out. It's really easy to do (It only takes one line) but it might be worth making sure that a connection is not left open by logging out before the ProcessClose in _SFTP_Close. Alternitively, you could create an _SFTP_Disconnect function.

The line that does this is

stdinWrite($hSession, "bye")

Not a big contribution I know, but I hope it helps! Great code, very helpful.

Share this post


Link to post
Share on other sites

Posted (edited)

Hi Lupo73 for this great UDF !

But i have a very annoying bug with it !

When i use '_SFTP_ListToArray' or '_SFTP_ListToArrayEx', it return me only one file, but with filezilla i can see two files !

Can you check this ? Thanks

For more information, i have put a debug line in your UDF

Line 1032 :

$sLine &= StdoutRead($hConnection)

ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sLine = ' & $sLine & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

And here is the answer :

$sLine = Listing directory /Deals/Out

drwxrwx--- 1 no-user no-group 0 Jan 1 1970 ..

-rwxrwx--- 1 no-user no-group 5500 Oct 26 14:07 xxxx.xml

-rwxrwx--- 1 no-user no-group 6159 Oct 26 14:08 xxxxx.xml

psftp>

so the UDF see the two file, but in the array i have only one

I don't find but the problem if after ligne 1032 when you transforme you stringplit to $aarray

I have solve my problem by working the stingsplit by myself, but it will be 'more clean' is you can correct the udf ^^

Edited by pinkfoyd

Share this post


Link to post
Share on other sites

Posted (edited)

Hi!

Used the SFTPEx.au3 to play with moving files from an SFTP server and found 2 issues.

_SFTP_ListToArray and _SFTP_ListToArrayEx both misses the 2 first files on the FTP. I rewrote them thus:

Func _SFTP_ListToArrayEx($hConnection, $sRemoteDir = "", $ReturnType = 0, $fTimeFormat = 1)
Local $aArray[1][5]
If ProcessExists($hConnection) = 0 Then
$aArray[0][0] = 0
Return SetError(1, 0, $aArray)
EndIf

If $ReturnType < 0 Or $ReturnType > 2 Then
$aArray[0][0] = 0
Return SetError(4, 0, $aArray)
EndIf

Local $sLine, $sTime, $aStringSplit, $aSubStringSplit, $B, $iArrayIndex = 0
If $sRemoteDir <> "" Then
$sRemoteDir = ' "' & $sRemoteDir & '"'
EndIf
StdinWrite($hConnection, 'ls' & $sRemoteDir & @CRLF)
While 1
$sLine &= StdoutRead($hConnection)
If ProcessExists($hConnection) = 0 Then
$aArray[0][0] = 0
Return SetError(1, 0, $aArray)
ElseIf StringInStr($sLine, "Unable to open") Then
$aArray[0][0] = 0
Return SetError(2, 0, $aArray)
ElseIf StringInStr($sLine, "Multiple-level wildcards") Or StringInStr($sLine, ": canonify:") Then
$aArray[0][0] = 0
Return SetError(5, 0, $aArray)
ElseIf StringInStr($sLine, "psftp>") Then
ExitLoop
EndIf
Sleep(10)
WEnd

$aStringSplit = StringSplit(StringStripCR($sLine), @LF)
If IsArray($aStringSplit) = 0 Then
$aArray[0][0] = 0
Return SetError(3, 0, $aArray)
EndIf
; CHANGED BY TCR
; $aArray[0][0] = $aStringSplit[0] - 4
$aArray[0][0] = $aStringSplit[0] - 2
If $aArray[0][0] < 1 Then
$aArray[0][0] = 0
Return $aArray
EndIf
ReDim $aArray[$aArray[0][0] + 1][5]

; For $A = 4 To $aStringSplit[0] - 1
For $A = 2 To $aStringSplit[0] - 1
If ($ReturnType = 1 And StringLeft($aStringSplit[$A], 1) <> "d") Or ($ReturnType = 2 And StringLeft($aStringSplit[$A], 1) <> "-") Then
$iArrayIndex += 1
$aArray[0][0] -= 1
ContinueLoop
EndIf
; $B = $A - 3 - $iArrayIndex
$B = $A - 1 - $iArrayIndex
$aSubStringSplit = _StringExplode(StringStripWS($aStringSplit[$A], 7), " ", 8)
If UBound($aSubStringSplit) < 9 Then
$iArrayIndex += 1
$aArray[0][0] -= 1
ContinueLoop
EndIf
$aArray[$B][0] = $aSubStringSplit[8]
$aArray[$B][1] = 0
If StringLeft($aSubStringSplit[0], 1) <> "d" Then ; Is A File.
$aArray[$B][1] = $aSubStringSplit[4]
EndIf
$aArray[$B][2] = $aSubStringSplit[0]
$sTime = ""
$aSubStringSplit[6] = StringRight("0" & $aSubStringSplit[6], 2)
If StringInStr($aSubStringSplit[7], ":") Then ; Is A Time.
$sTime = " " & $aSubStringSplit[7]
$aSubStringSplit[7] = @YEAR
If _DateDiff('n', $aSubStringSplit[7] & "/" & __MonthToNumber($aSubStringSplit[5]) & "/" & $aSubStringSplit[6] & $sTime, _NowCalc()) < 0 Then
$aSubStringSplit[7] -= 1
EndIf
EndIf
If $fTimeFormat = 1 Then
$aArray[$B][3] = $aSubStringSplit[7] & "/" & __MonthToNumber($aSubStringSplit[5]) & "/" & $aSubStringSplit[6] & $sTime
Else
$aArray[$B][3] = __MonthToNumber($aSubStringSplit[5]) & "/" & $aSubStringSplit[6] & "/" & $aSubStringSplit[7] & $sTime
EndIf
; <<<<<<<<<<<<<<<<<<<<<<<<<<< it may needs to update time based on timezone offset
$aArray[$B][4] = $aSubStringSplit[2] & " " & $aSubStringSplit[3]
Next
ReDim $aArray[$aArray[0][0] + 1][5]

Return $aArray
EndFunc ; ==>_SFTP_ListToArrayEx

And

Func _SFTP_ListToArray($hConnection, $sRemoteDir = "", $ReturnType = 0)
Local $aArray[1]
If ProcessExists($hConnection) = 0 Then
$aArray[0] = 0
Return SetError(1, 0, $aArray)
EndIf

If $ReturnType < 0 Or $ReturnType > 2 Then
$aArray[0] = 0
Return SetError(4, 0, $aArray)
EndIf

Local $sLine, $aStringSplit, $aSubStringSplit, $B, $iArrayIndex = 0
If $sRemoteDir <> "" Then
$sRemoteDir = ' "' & $sRemoteDir & '"'
EndIf
StdinWrite($hConnection, 'ls' & $sRemoteDir & @CRLF)
While 1
$sLine &= StdoutRead($hConnection)
If ProcessExists($hConnection) = 0 Then
$aArray[0] = 0
Return SetError(1, 0, $aArray)
ElseIf StringInStr($sLine, "Unable to open") Then
$aArray[0] = 0
Return SetError(2, 0, $aArray)
ElseIf StringInStr($sLine, "Multiple-level wildcards") Or StringInStr($sLine, ": canonify: ") Then
$aArray[0] = 0
Return SetError(5, 0, $aArray)
ElseIf StringInStr($sLine, "psftp>") Then
ExitLoop
EndIf
Sleep(10)
WEnd
;MsgBox(0, "Inside _SFTP_ListToArray", "Have Just got this file list: " & @CRLF & $sLine)

$aStringSplit = StringSplit(StringStripCR($sLine), @LF)
If IsArray($aStringSplit) = 0 Then
$aArray[0] = 0
Return SetError(3, 0, $aArray)
EndIf
; CHANGED BY TCR:
; Removing first 4 lines... why???
; Lets Try only to remove 2 line and see...
; $aArray[0] = $aStringSplit[0] - 4
$aArray[0] = $aStringSplit[0] - 2
If $aArray[0] < 1 Then
$aArray[0] = 0
Return $aArray
EndIf
ReDim $aArray[$aArray[0] + 1]

; For $A = 4 To $aStringSplit[0] - 1
For $A = 2 To $aStringSplit[0] - 1
If ($ReturnType = 1 And StringLeft($aStringSplit[$A], 1) <> "d") Or ($ReturnType = 2 And StringLeft($aStringSplit[$A], 1) <> "-") Then
$iArrayIndex += 1
$aArray[0] -= 1
ContinueLoop
EndIf
; $B = $A - 3 - $iArrayIndex
$B = $A - 1 - $iArrayIndex
$aSubStringSplit = _StringExplode(StringStripWS($aStringSplit[$A], 7), " ", 8)
If UBound($aSubStringSplit) < 9 Then
$iArrayIndex += 1
$aArray[0] -= 1
ContinueLoop
EndIf
$aArray[$B] = $aSubStringSplit[8]
Next
ReDim $aArray[$aArray[0] + 1]

Return $aArray
EndFunc ; ==>_SFTP_ListToArray

Dont know why you have 3 garbage lines and I only get one. Might be that my test server didnt show hidden files or that it was on a Danish computer. Just guessing.

My suggestion would be to rewrite these functions to take ALL lines and then only process lines starting with d??? or -??? to filter out all garbage lines.

Kind Regards,

Thomas

PS. Great Work!!! DS.

Edited by AngelsFlyHigh

Share this post


Link to post
Share on other sites

Posted (edited)

Thanks for your reports, I'll fix the UDF. If you made other improvements in the meanwhile, they are welcome!

Edit: I updated the UDF following your advices.. let me know!

Edited by Lupo73

Share this post


Link to post
Share on other sites

Posted (edited)

Hi Lupo.. maybe you should add the red marked code below to your functions, because if someone (like me) has installed putty and uses a standard session, connecting to any server will fail, because the standard session is being loaded by default and the desired StdOut "psftp>" never appears.

This is the solution to the problem kazki has described in his post.

Cheers

trainer

Func _SFTP_Open($sPath = 'psftp.exe')

Local $hSession = Run($sPath & " -load null", "", @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD)

[...]

Func _SFTP_Connect($hSession, $sServerName, $sUsername = "", $sPassword = "", $iServerPort = 0)

If ProcessExists($hSession) = 0 Then

Return SetError(1, 0, 0)

EndIf

If $iServerPort = 0 Then

$iServerPort = ""

EndIf

Local $sLine, $sStringSplit

StdinWrite($hSession, 'open ' & $sServerName & ' ' & $iServerPort & @CRLF)

$wait_for_key_saving = 0

$save_key_if_not_saved = True

While 1

;If after 5 seconds no psftp>-prompt appears, putty is probably waiting for the answer whether the (new) ssh-key should be saved in the registry (this appears every time, if the key is not being saved) --> answer the question with 'y' (once).

$wait_for_key_saving += 1

If $wait_for_key_saving >= 500 And $save_key_if_not_saved Then

StdinWrite($hSession, 'y' & @CRLF)

$save_key_if_not_saved = False

EndIf

$sLine = StdoutRead($hSession)

[...]

Edited by trainer

Share this post


Link to post
Share on other sites

Posted

Note that I forgot to write an answer, but the last "red corrections" are implemented in the code.

Share this post


Link to post
Share on other sites

Posted

i like it when it all works the first time :)

very nice work Adventurer -- you made it really easy to use psftp

Share this post


Link to post
Share on other sites

Posted (edited)

Hi Lupo73,

I have been doing some tests with your SFTPEx UDF and I find it very useful. I appreciate that you are the first one to come up with a UDF covering SFTP, and you've got my :thumbsup:  for this.

I found an inconsistency in _SFTP_FileMove which I tried to correct, see code hereunder.

The first issue occurred while trying to rename a file to an already existing one, then the function did not return an error, but the file was not renamed.

The second issue occurred while moving (multiple) files to a directory using wildcards, and one of the files already existed in the destination directory.

I reshuffled the If/Else loop so that it tackles all error situations and I believe it captures all errors now.

I added extra error return values, I am not sure if Error 5 is still needed but I left it there, it doesn't harm anyway.

Correct Version (added dummy stdoutread at the end so that other functions are not affected by the remaining data in the buffer,  sorry again)

Update 12/12/2013

New issue found when trying to move multiple files with wildcards.

In some cases, for example when using wildcards, file processing time increases, StdoutRead could still be empty, so i added a loop waiting fro the 'psftp>' prompt before going forward with error checking.

One possible issue: if the prompt never comes, the function will ends into an indefinite loop.  I have no solution for this but adding a time-out as a parameter would be a partial answer.  Question is then 'which time out should be used?'

You will also notice that I empty the console buffer before and at the end of the function. I noticed that  previous failed function, like an error on creating a directory will abort the process with an error but do not clear the console buffer.  I would recommend to always clear the console on exit of a function.


; #FUNCTION# ====================================================================================================================
; Name...........: _SFTP_FileMove
; Description ...: Move/Rename a file on a SFTP server.
; Syntax.........: _SFTP_FileMove ( $hConnection, $sSourceFile, $sDestinationFile )
; Parameters ....: $hConnection - as returned by _SFTP_Connect().
;                  $sSourceFile - The source file to move/rename. Wildcards are allowed if move to a directory
;                  $sDestinationFile - The destination file or directory to move/rename.
; Return values .: Success - 1
;                  Failure - 0, sets @error
;                  |1 - The connection is closed
;                  |2 - File not found
;                  |3 - Failure to rename or move a file. In case of wildcards, failure to move at least one file.
;                  |4 - no file name matched the source file name(s)
;                  |5 - Other error
; Author ........: Lupo73, GreenCan
; Modified.......: Corrected issue while trying to rename a file to a new file that already exists
;                   Issue with StdoutRead being empty still, causing malfunction of _SFTP_FileMove, added wait loop.
; Remarks .......:
; Related .......: _SFTP_Connect
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _SFTP_FileMove($hConnection, $sSourceFile, $sDestinationFile)
    If ProcessExists($hConnection) = 0 Then
        Return SetError(1, 0, 0)
    EndIf

    Local $sLine
    $sLine = StdoutRead($hConnection) ; dummy StdoutRead to empty the buffer from previously failed commands

    StdinWrite($hConnection, 'mv "' & $sSourceFile & '" "' & $sDestinationFile & '"' & @CRLF)
    ;loop required to wait for console response
    While 1
        sleep(100)
        $sLine = StdoutRead($hConnection, True) ; keep the stream buffer intact
;~         If $sLine <> "" Then ExitLoop
        If StringInStr($sLine, "psftp>") Then ExitLoop
    WEnd
    While 1
        $sLine = StdoutRead($hConnection)
        If ProcessExists($hConnection) = 0 Then
            Return SetError(1, 0, 0)
        ElseIf StringInStr($sLine, $sSourceFile & " -> ") Then
            ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " " & $sLine & @CR)
            ContinueLoop
        ElseIf StringInStr($sLine, "no such file or directory") Then
            ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " " & $sLine & @CR)
            Return SetError(2, 0, 0)
        ElseIf StringInStr($sLine, "failure") Then
            ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " " & $sLine & @CR)
            Return SetError(3, 0, 0)
        ElseIf StringInStr($sLine, "nothing matched") Then
            ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " " & $sLine & @CR)
            Return SetError(4, 0, 0)
        ElseIf StringInStr($sLine, "psftp>") Then
            ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " " & $sLine & @CR)
            ExitLoop
        ElseIf $sLine <> "" Then ; any other failure
            ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " " & $sLine & @CR)
            Return SetError(5, 0, 0)
        ElseIf $sLine == "" Then
            ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " " & $sLine & @CR)
            ExitLoop
        EndIf
        Sleep(10)
        ;If Not @Compiled Then ConsoleWrite( @ScriptLineNumber & " >" & $sLine & "<" & @CR)
    WEnd
    $sLine = StdoutRead($hConnection) ; dummy StdoutRead to empty the buffer
    Return 1
EndFunc   ; ==>_SFTP_FileMove

I hope this helps.

GreenCan

Edited by GreenCan

Share this post


Link to post
Share on other sites

Posted (edited)

An example file is always better...  here is one

remember to fill in these lines:

Global $sServerName = "server name"                 ; ==> fill in
Global $sUsername = "user name"                        ; ==> fill in
Global $sPassword = "user password"                  ; ==> fill in
Global $iServerPort = 22                                       ; ==> fill in optionally, default should be 22, not 0 as far as i know
Global $cRemoteDir = "/some remote paths"        ; ==> fill in

and create a 'test.txt' file in your script folder.

Update 12/12/2013

cleanup up example

SFTPEx example - Generic.au3

Edited by GreenCan
MuffettsMan likes this

Share this post


Link to post
Share on other sites

Posted (edited)

Sorry for the late and thanks for these improvements!

A question: why it is needed a second loop before the main one? the check of the "psftp>" string is already included in the main loop.

If you would like to help me to improve and complete this UDF, I think it could be useful to several users.  :thumbsup:

Edited by Lupo73

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0