FTP module
							parent
							
								
									2f23ee4818
								
							
						
					
					
						commit
						7518d2d0dc
					
				|  | @ -0,0 +1,2 @@ | |||
| ftp_test | ||||
| *.bak | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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