3 # The information to write this program was gathered from RFC 959,
4 # but this is not a complete implementation! Yet it shows how a simple
5 # FTP client can be built, and you are welcome to extend it to suit
8 # How it works (assuming you've read the RFC):
10 # User commands are passed uninterpreted to the server. However, the
11 # user never needs to send a PORT command. Rather, the client opens a
12 # port right away and sends the appropriate PORT command to the server.
13 # When a response code 150 is received, this port is used to receive
14 # the data (which is written to stdout in this version), and when the
15 # data is exhausted, a new port is opened and a corresponding PORT
16 # command sent. In order to avoid errors when reusing ports quickly
17 # (and because there is no s.getsockname() method in Python yet) we
18 # cycle through a number of ports in the 50000 range.
21 import sys
, posix
, string
27 # Default port numbers used by the FTP protocol.
30 FTP_DATA_PORT
= FTP_PORT
- 1
32 # Change the data port to something not needing root permissions.
34 FTP_DATA_PORT
= FTP_DATA_PORT
+ 50000
37 # Main program (called at the end of this file).
40 hostname
= sys
.argv
[1]
44 # Control process (user interface and user protocol interpreter).
46 def control(hostname
):
48 # Create control connection
50 s
= socket(AF_INET
, SOCK_STREAM
)
51 s
.connect((hostname
, FTP_PORT
))
52 f
= s
.makefile('r') # Reading the replies is easier from a file...
59 if code
in ('221', 'EOF'): break
71 # Create a new data port and send a PORT command to the server for it.
72 # (Cycle through a number of ports to avoid problems with reusing
73 # a port within a short time.)
77 def newdataport(s
, f
):
79 port
= nextport
+ FTP_DATA_PORT
80 nextport
= (nextport
+1) % 16
81 r
= socket(AF_INET
, SOCK_STREAM
)
82 r
.bind((gethostbyname(gethostname()), port
))
84 sendportcmd(s
, f
, port
)
88 # Send an appropriate port command.
90 def sendportcmd(s
, f
, port
):
91 hostname
= gethostname()
92 hostaddr
= gethostbyname(hostname
)
93 hbytes
= string
.splitfields(hostaddr
, '.')
94 pbytes
= [repr(port
/256), repr(port
%256)]
95 bytes
= hbytes
+ pbytes
96 cmd
= 'PORT ' + string
.joinfields(bytes
, ',')
101 # Process an ftp reply and return the 3-digit reply code (as a string).
102 # The reply should be a line of text starting with a 3-digit number.
103 # If the 4th char is '-', it is a multi-line reply and is
104 # terminate by a line starting with the same 3-digit number.
105 # Any text while receiving the reply is echoed to the file.
109 if not line
: return 'EOF'
115 if not line
: break # Really an error
117 if line
[:3] == code
and line
[3:4] != '-': break
121 # Get the data from the data connection.
124 print '(accepting data connection)'
125 conn
, host
= r
.accept()
126 print '(data connection accepted)'
128 data
= conn
.recv(BUFSIZE
)
130 sys
.stdout
.write(data
)
131 print '(end of data connection)'
133 # Get a command from the user.
138 line
= raw_input('ftp.py> ')
144 # Call the main program.