interactive FTP
[1/3] from: sqlab:gmx at: 15-Jun-2001 10:39
Hi
Inspired by the telnet scheme from Frank Sievertsen, the recent discussion
about FTP and some problems I had last year with non standard FTP servers I
finished this script now.
(Un)fortunately I do not use a proxy, so don't ask me about that feature.
I hope the formatting is not to much changed by my remailer program.
AR
REBOL [title: "mFT"
date: 15-June-2001
Author: "Anton Reisacher"
Email: [sqlab--gmx--net]
File: %mftp.r
Version: 1.0
Description: {minimal interactive ftp scheme without conversion to Rebol
standards
should allow usage of any ftp command supported by your host,
even Xxxx commands
for example just do %mftp.r
port: open mftp://user:[pass--host]
insert port "HELP"
print copy port ; and you see what your server supports
insert port "XCWD $volume.subvolume" ; if your host uses a
different filesystem
copy port
insert port "LIST"
print copy port ; complete listing
got: make string! 1000
insert port "TYPE I"
copy port
insert port "RETR file"
while [get: copy port ] [append got get] ; retrieve all data
insert port "TYPE A" ; take care of CRLF in ASCII mode by
yourself
copy port
insert port "STOR Test"
insert port data ; you can do repeated inserts until you do a
copy
copy port
close port
Proxies are not supported.
}
Category: 'ftp
]
mftp: make root-protocol [
scheme: 'mftp
port-id: 21
port-flags: system/standard/port-flags/pass-thru
open: func [port [port! url!]
/local tmp
] [
open-proto port
port/sub-port/state/flags: port/sub-port/state/flags or 2051
port/state/flags: port/state/flags or
system/standard/port-flags/direct or 2051
if port/user [
tmp: make string! 512
until [
if not wait [port/sub-port port/timeout] [net-error
Timeout
]
any [find/any append tmp copy port"log?n"
find/any tmp "user"
find/any tmp "name"
]
]
system/words/insert port/sub-port join "USER " port/user
]
if port/pass [
tmp: make string! 256
until [
if not wait [port/sub-port port/timeout] [net-error
Timeout
]
find/any append tmp copy port"pass"
]
system/words/insert port/sub-port join "PASS " port/pass
]
tmp: make string! 128
until [
if not wait [port/sub-port port/timeout] [net-error "Timeout"]
find/any append tmp copy port"230"
]
port/user-data: tmp
]
copy: func [port [port!] /part len [integer!] /local tmp ;leng
] [ tmp: make string! 4096
either port/local-service = 'in [
port/local-service: none
system/words/close port/remote-service
port/remote-service: none
if not wait [port/sub-port port/timeout] [net-error "Timeout"]
net-utils/net-log append tmp system/words/copy port/sub-port
] [ either port/remote-service [
all [
port/local-service <> 'out
port/local-service: 'out
not wait [port/remote-service port/timeout]
net-error "Timeout"
]
either part [
tmp: system/words/copy/part port/remote-service len
][
tmp: system/words/copy port/remote-service
]
either tmp [
; leng: :leng + length? tmp
net-utils/net-log to-string tmp
] [
tmp: net-utils/net-log system/words/copy port/sub-port
; if leng <> check length?
system/words/close port/remote-service
port/remote-service: none
]
] [either port/user-data [
tmp: port/user-data
port/user-data: none
tmp
] [
net-utils/net-log append tmp system/words/copy port/sub-port
] ] ]
]
insert: func [port [port!] data /local listen-port tmp] [
either all [port/remote-service
port/local-service <> 'out
port/local-service: 'in
] [
system/words/insert port/remote-service data
] [
error? try [catch [close port/remote-service]]
port/remote-service: none
port/local-service: none
; either all [not proxy not passive] [listen-port: get-port
port] [data-connect]
listen-port: system/words/open/binary/direct tcp://:0
tmp: form reduce port/sub-port/local-ip
append tmp rejoin ["." form to-integer (listen-port/port-id /
256)
"." (listen-port/port-id // 256)
]
replace/all tmp #"." #","
system/words/insert port/sub-port net-utils/net-log join "PORT "
tmp
tmp: make string! 128
until [
if not wait [port/sub-port port/timeout] [net-error
Timeout
]
find/any append tmp copy port"200"
]
system/words/insert port/sub-port net-utils/net-log data
tmp: make string! 128
until [
if not wait [port/sub-port port/timeout] [net-error
Timeout
]
find/any append tmp copy port"???"
]
either find tmp "150" [
port/remote-service: first listen-port
] [
port/user-data: tmp
port/remote-service: none
]
system/words/close listen-port
]
]
close: func [port [port!] ] [
system/words/insert port/sub-port net-utils/net-log"QUIT"
system/words/close port/sub-port
error? try [catch [system/words/close data/remote-service]]
]
net-utils/net-install 'mFtp self 21
]
[2/3] from: fantam:mailandnews at: 18-Jun-2001 1:30
Very good stuff, AR. Thanks.
I was also left disappointed by the built-in ftp features. Nothing
wrong with the design, but 'exists in the ftp context never worked for
me, for example. At RT, they're aware of ftp related bugs. I don't
understand why they don't fix them. Probably too busy. Other
priorities, maybe.
[3/3] from: fantam:mailandnews at: 24-Jun-2001 16:02
Excellent. Thanks again, AR. I noticed that Jeff's post on schemes has
made it to an article on www.rebolforces.com.
I wrote a couple of functions that are meant to be used in conjunction
with your scheme (mftp).
exists: func [port [port!] file [file! string!] /local content] [
insert port "LIST"
content: copy port
content: parse/all content "^/"
foreach line content [
if (last parse line "") = to-string file [return true]
]
return false
]
isdir: func [port [port!] file [file! string!] /local content] [
insert port "LIST"
content: copy port
content: parse/all content "^/"
foreach line content [
if find/case line file [
if (first line) = #"d" [return true]
]
]
return false
]
ftp-bin-upload: func [port [port!] file [file!] /local data] [
data: read/binary file
insert port "TYPE I"
print copy port
insert port reform ["STOR" file]
insert port data
print copy port
]
makedir: func [port [port!] file [file! string!]] [
insert port reform ["MKD" file]
print copy port
]
The next one is a more sophisticated exists function, that can accept a url,
like ftp://ftp.ftp.com/test/test/test.txt and it will navigate to the
needed directory, and tell you if the file exists. If you give a url
like ftp://ftp.ftp.com/test/test/, it should work as well, and tell
you whether the directory path exists or not.
exists: func [port [port!] file [file! string! url!] /local content current-exists path]
[
current-exists: func [file] [
insert port "LIST"
content: copy port
content: parse/all content "^/"
foreach line content [
if (last parse line "") = to-string file [return true]
]
return false
]
cdup: does [
insert port "CWD /"
copy port
]
either url? file [
url-obj: make object! [user: pass: host: port-id: path: target: none]
net-utils/url-parser/parse-url url-obj file
path: parse url-obj/path "/"
foreach item path [
if (current-exists port item) [
insert port reform ["CWD" item]
if find/any copy/part copy port 3 "5??" [cdup return false]
]
]
either not none? url-obj/target [current-exists url-obj/target] [cdup return true]
]
[current-exists file]
]
Comments are welcome. I'll probably write more functions like the
above. If anyone's interested, let me know.
that's it for now.
fantam