mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
FTP module
This commit is contained in:
parent
2f23ee4818
commit
7518d2d0dc
2
vlib/ftp/.gitignore
vendored
Normal file
2
vlib/ftp/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ftp_test
|
||||||
|
*.bak
|
279
vlib/ftp/ftp.v
Normal file
279
vlib/ftp/ftp.v
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
basic ftp module
|
||||||
|
RFC-959
|
||||||
|
https://tools.ietf.org/html/rfc959
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
ftp.connect(host)
|
||||||
|
ftp.login(user,passw)
|
||||||
|
pwd := ftp.pwd()
|
||||||
|
ftp.cd(folder)
|
||||||
|
dtp := ftp.pasv()
|
||||||
|
ftp.dir()
|
||||||
|
ftp.get(file)
|
||||||
|
dtp.read()
|
||||||
|
dtp.close()
|
||||||
|
ftp.close()
|
||||||
|
*/
|
||||||
|
|
||||||
|
module ftp
|
||||||
|
|
||||||
|
import net
|
||||||
|
|
||||||
|
const (
|
||||||
|
Connected = 220
|
||||||
|
SpecifyPassword = 331
|
||||||
|
LoggedIn = 230
|
||||||
|
LoginFirst = 503
|
||||||
|
Anonymous = 530
|
||||||
|
OpenDataConnection = 150
|
||||||
|
CloseDataConnection = 226
|
||||||
|
CommandOk = 200
|
||||||
|
Denied = 550
|
||||||
|
PassiveMode = 227
|
||||||
|
Complete = 226
|
||||||
|
)
|
||||||
|
|
||||||
|
struct DTP {
|
||||||
|
mut:
|
||||||
|
sock net.Socket
|
||||||
|
ip string
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (dtp DTP) read() []byte {
|
||||||
|
mut data := []byte
|
||||||
|
for {
|
||||||
|
buf,len := dtp.sock.recv(1024)
|
||||||
|
if len == 0 { break }
|
||||||
|
|
||||||
|
for i:=0;i<len;i++ {
|
||||||
|
data << buf[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (dtp DTP) close() {
|
||||||
|
dtp.sock.close() or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FTP {
|
||||||
|
mut:
|
||||||
|
sock net.Socket
|
||||||
|
buffer_size int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() FTP {
|
||||||
|
mut f := FTP{}
|
||||||
|
f.buffer_size = 1024
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (ftp FTP) write(data string) ?int {
|
||||||
|
$if debug {
|
||||||
|
println('FTP.v >>> $data')
|
||||||
|
}
|
||||||
|
n := ftp.sock.send_string(data + '\n') or {
|
||||||
|
return error('cannot send data')
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (ftp FTP) read() (int,string) {
|
||||||
|
mut data := ftp.sock.read_line()
|
||||||
|
$if debug {
|
||||||
|
println('FTP.v <<< $data')
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.len < 5 {
|
||||||
|
return 0,''
|
||||||
|
}
|
||||||
|
|
||||||
|
code := data[0..3].int()
|
||||||
|
if data[4] == `-` {
|
||||||
|
for {
|
||||||
|
data = ftp.sock.read_line()
|
||||||
|
if data[0..3].int() == code {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code,data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ftp mut FTP) connect(ip string) bool {
|
||||||
|
sock := net.dial(ip, 21) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ftp.sock = sock
|
||||||
|
|
||||||
|
code,_ := ftp.read()
|
||||||
|
if code == Connected {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ftp FTP) login(user, passwd string) bool {
|
||||||
|
|
||||||
|
ftp.write('USER '+user) or {
|
||||||
|
println('ERROR sending user')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mut data := ''
|
||||||
|
mut code := 0
|
||||||
|
|
||||||
|
code,data = ftp.read()
|
||||||
|
if code == LoggedIn {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if code != SpecifyPassword {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ftp.write('PASS '+passwd) or {
|
||||||
|
println('ERROR sending password')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
code,data = ftp.read()
|
||||||
|
|
||||||
|
if code == LoggedIn {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ftp FTP) close() {
|
||||||
|
send_quit := 'QUIT\r\n'
|
||||||
|
ftp.sock.send_string(send_quit) or {}
|
||||||
|
ftp.sock.close() or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ftp FTP) pwd() string {
|
||||||
|
ftp.write('PWD') or {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
_,data := ftp.read()
|
||||||
|
spl := data.split('"')
|
||||||
|
if spl.len >= 2 {
|
||||||
|
return spl[1]
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ftp FTP) cd(dir string) {
|
||||||
|
ftp.write('CWD $dir') or { return }
|
||||||
|
mut code, mut data := ftp.read()
|
||||||
|
match code {
|
||||||
|
Denied {
|
||||||
|
println("CD $dir denied!")
|
||||||
|
}
|
||||||
|
Complete {
|
||||||
|
code,data = ftp.read()
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
|
||||||
|
println('cd $data')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_dtp(msg string) ?DTP {
|
||||||
|
// it receives a control message 227 like:
|
||||||
|
// '227 Entering Passive Mode (209,132,183,61,48,218)'
|
||||||
|
|
||||||
|
if !msg.contains('(') || !msg.contains(')') || !msg.contains(',') {
|
||||||
|
return error('bad message')
|
||||||
|
}
|
||||||
|
|
||||||
|
t := msg.split('(')[1].split(')')[0].split(',')
|
||||||
|
ip := t[0]+'.'+t[1]+'.'+t[2]+'.'+t[3]
|
||||||
|
port := t[4].int()*256+t[5].int()
|
||||||
|
|
||||||
|
sock := net.dial(ip, port) or {
|
||||||
|
return error('Cant connect to the data channel')
|
||||||
|
}
|
||||||
|
|
||||||
|
dtp := DTP {
|
||||||
|
sock : sock
|
||||||
|
ip: ip
|
||||||
|
port: port
|
||||||
|
}
|
||||||
|
return dtp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (ftp FTP) pasv() ?DTP {
|
||||||
|
ftp.write('PASV') or {}
|
||||||
|
code,data := ftp.read()
|
||||||
|
println("pass: $data")
|
||||||
|
|
||||||
|
if code != PassiveMode {
|
||||||
|
return error('pasive mode not allowed')
|
||||||
|
}
|
||||||
|
|
||||||
|
dtp := new_dtp(data)
|
||||||
|
|
||||||
|
return dtp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ftp FTP) dir() ?[]string {
|
||||||
|
dtp := ftp.pasv() or {
|
||||||
|
return error('cannot establish data connection')
|
||||||
|
}
|
||||||
|
|
||||||
|
ftp.write('LIST') or {}
|
||||||
|
code,_ := ftp.read()
|
||||||
|
if code == Denied {
|
||||||
|
return error('list denied')
|
||||||
|
}
|
||||||
|
if code != OpenDataConnection {
|
||||||
|
return error('data channel empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
list_dir := dtp.read()
|
||||||
|
result,_ := ftp.read()
|
||||||
|
if result != CloseDataConnection {
|
||||||
|
println('LIST not ok')
|
||||||
|
}
|
||||||
|
dtp.close()
|
||||||
|
|
||||||
|
mut dir := []string
|
||||||
|
sdir := string(byteptr(list_dir.data))
|
||||||
|
for lfile in sdir.split('\n') {
|
||||||
|
if lfile.len >1 {
|
||||||
|
spl := lfile.split(' ')
|
||||||
|
dir << spl[spl.len-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ftp FTP) get(file string) ?[]byte {
|
||||||
|
dtp := ftp.pasv() or {
|
||||||
|
return error('cant stablish data connection')
|
||||||
|
}
|
||||||
|
|
||||||
|
ftp.write('RETR $file') or {}
|
||||||
|
code,_ := ftp.read()
|
||||||
|
|
||||||
|
if code == Denied {
|
||||||
|
return error('permission denied')
|
||||||
|
}
|
||||||
|
|
||||||
|
if code != OpenDataConnection {
|
||||||
|
return error('data connection not ready')
|
||||||
|
}
|
||||||
|
|
||||||
|
blob := dtp.read()
|
||||||
|
dtp.close()
|
||||||
|
|
||||||
|
return blob
|
||||||
|
}
|
57
vlib/ftp/ftp_test.v
Normal file
57
vlib/ftp/ftp_test.v
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
|
||||||
|
import ftp
|
||||||
|
|
||||||
|
fn test_all() {
|
||||||
|
mut ftp := ftp.new()
|
||||||
|
|
||||||
|
// ftp.rediris.org
|
||||||
|
connected := ftp.connect('ftp.redhat.com')
|
||||||
|
assert connected
|
||||||
|
if connected {
|
||||||
|
println("connected")
|
||||||
|
|
||||||
|
loggedin := ftp.login('ftp','ftp')
|
||||||
|
assert loggedin
|
||||||
|
if loggedin {
|
||||||
|
println('logged-in')
|
||||||
|
|
||||||
|
pwd := ftp.pwd()
|
||||||
|
println('pwd: $pwd')
|
||||||
|
|
||||||
|
ftp.cd('/')
|
||||||
|
|
||||||
|
folder := ftp.dir() or {
|
||||||
|
eprintln('cannot list folder')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for file in folder {
|
||||||
|
println(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
ftp.cd('/suse/linux/enterprise/11Server/en/SAT-TOOLS/SRPMS/')
|
||||||
|
|
||||||
|
dir_list := ftp.dir() or {
|
||||||
|
eprintln('cannot list folder')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert dir_list.len > 5
|
||||||
|
println('$dir_list.len files:')
|
||||||
|
for file in dir_list {
|
||||||
|
println('$file')
|
||||||
|
}
|
||||||
|
|
||||||
|
blob := ftp.get('katello-host-tools-3.3.5-8.sles11_4sat.src.rpm') or {
|
||||||
|
eprintln("couldn't download it")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert blob.len > 1024
|
||||||
|
|
||||||
|
println('downloaded $blob.len bytes')
|
||||||
|
}
|
||||||
|
ftp.close()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user